/*
 * Copyright (c) 2018-2024 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

/* strcasestr function need _GNU_SOURCE definition */
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include "xdrbase.h"
#include "channel.h"
#include "hash.h"
#include "yaml.h"
#include "limit.h"
#include "api.h"

#define ILLEGAL_CHARS     ".!-=+*/[]@:&|{}'`\""

#define NULL_POINTER_STR     "%s: %s is Null"
#define IN_QUEUE_ADMIN_STR   "in queue's administrator list"
#define IN_QUEUE_HOST_STR    "in queue's HOSTS list"

#define FLAG_1  RESOURCE_FLAG_BUILTIN | RESOURCE_FLAG_GLOBAL
#define FLAG_2  FLAG_1 | RESOURCE_FLAG_DYNAMIC

#define HOSTGROUP_NAME_INDEX       0
#define HOSTGROUP_MEMBER_INDEX     1
#define HOSTGROUP_MAXSLOT_INDEX    2

struct resource_definition builtInIndices[] = {
    {(char *)"r1m", (char *)"1-minute CPU run queue length",
     TYPE_NUMERIC, INCR, FLAG_2 | RESOURCE_FLAG_RELEASE, 15},
    {(char *)"r5m", (char *)"5-minute CPU run queue length",
     TYPE_NUMERIC, INCR, FLAG_2 | RESOURCE_FLAG_RELEASE, 15},
    {(char *)"r15m", (char *)"15-minute CPU run queue length",
     TYPE_NUMERIC, INCR, FLAG_2 | RESOURCE_FLAG_RELEASE, 15},
    {(char *)"ut", (char *)"CPU utilization (0.0 to 1.0)",
     TYPE_NUMERIC, INCR , FLAG_2, 15},
    {(char *)"pg", (char *)"Paging rate (pages/second)",
     TYPE_NUMERIC, INCR, FLAG_2, 15},
    {(char *)"io", (char *)"Disk IO rate (Kbytes/second)",
     TYPE_NUMERIC, INCR, FLAG_2, 15},
    {(char *)"tmp", (char *)"Disk space in /tmp (Mbytes)" ,
     TYPE_NUMERIC, DECR, FLAG_2 | RESOURCE_FLAG_CONSUMABLE, 120},
    {(char *)"swp", (char *)"Available swap space (Mbytes) (alias: swap)",
     TYPE_NUMERIC, DECR, FLAG_2 | RESOURCE_FLAG_CONSUMABLE, 15},
    {(char *)"mem", (char *)"Available memory (Mbytes)",
     TYPE_NUMERIC, DECR, FLAG_2 | RESOURCE_FLAG_CONSUMABLE, 15},
    {(char *)"ncpus", (char *)"Number of CPUs",
     TYPE_NUMERIC, DECR, FLAG_1, 0},
    {(char *)"maxmem", (char *)"Maximum memory (Mbytes)",
     TYPE_NUMERIC, DECR, FLAG_1, 0},
    {(char *)"maxswp", (char *)"Maximum swap space (Mbytes)",
     TYPE_NUMERIC, DECR, FLAG_1, 0},
    {(char *)"maxtmp", (char *)"Maximum /tmp space (Mbytes)",
     TYPE_NUMERIC, DECR, FLAG_1, 0},
    {(char *)"type", (char *)"Host type",
     TYPE_STRING,  NA, FLAG_1, 0},
    {(char *)"model", (char *)"Host model",
     TYPE_STRING,  NA, FLAG_1, 0},
    {(char *)"status", (char *)"Host status",
     TYPE_STRING,  NA, FLAG_1, 0},
    {(char *)"server", (char *)"Server host",
     TYPE_BOOLEAN, NA, FLAG_1, 0},
    {(char *)"hname", (char *)"Host name",
     TYPE_STRING,  NA, FLAG_1, 0},
    {(char *)"slots", (char *)"Available slots (JS slots resource)",
     TYPE_NUMERIC, DECR, FLAG_1 | RESOURCE_FLAG_FREE_SLOT, 0},
    {(char *)"maxslots", (char *)"Available MXJ slots (JS MXJ resource)",
     TYPE_NUMERIC, DECR, FLAG_1 | RESOURCE_FLAG_MAX_JOB_SLOT, 0},
    { NULL, NULL, TYPE_NUMERIC, INCR, FLAG_2, 0 }
};

extern char *run_group(const char *, char *);

struct sharedConf *gptrSharedConf=NULL;
struct clusterConf *gptrClusterConf=NULL;
struct QuickPoolParameter *gptrParamConf=NULL;

static struct quickpoolInfo qpinfo;
static struct hostConf *sptrHostConf=NULL;
static struct userConf *sptrUserConf=NULL;
static struct queueConf *sptrQueueConf=NULL;

static unsigned int sintMaxResource;
static unsigned int sintMaxLocation;

static HASH_TABLE_T *shashHost;
static struct QuickPoolHostInfo *sptrDefaultHost;
static HASH_TABLE_T *shashUnknownUsers;
static HASH_TABLE_T *shashDefinedUsers;

static bool_t parse_hosttype(yaml_parser_t *, char *);
static bool_t parse_hostmodel(yaml_parser_t *, char *);
static bool_t parse_resource(yaml_parser_t *, char *);
static bool_t parse_cluster(yaml_parser_t *, char *);

static int initialize_resource_table(void);
static bool_t parse_cluster_admin(yaml_parser_t *, char *);
static bool_t parse_cluster_param(yaml_parser_t *, char *);
static bool_t parse_node(yaml_parser_t *, char *);
static bool_t parse_resource_map(yaml_parser_t *, char *);
static bool_t add_resource_map(struct config_param *, char *);

static bool_t parse_jsnode(yaml_parser_t *, char *, struct quickpoolInfo *);
static bool_t parse_host_group(yaml_parser_t *, const char *);
 
static bool_t parse_user(yaml_parser_t *, const char *);
static bool_t parse_user_group(yaml_parser_t *, const char *);

static void initialize_hostlimit(struct QuickPoolHostInfo *);
static bool_t parse_queue(yaml_parser_t *, char *, struct quickpoolInfo *);
static char *parse_queue_admins(char *, char *, char *);

static char **expand_group(char *, int *);

static int create_userconf(void);
static int create_hostconf(void);

static char * put_inlist(char **, unsigned int *, const char *, const char *);

struct inNames {
    char* name;
    char* prf_level;
};
static int node_base_name(const char *name, LIST_T *namelist);
static void free_hostinfo(struct QuickPoolHostInfo *);

/**
 * @brief       Read the qp.shared file and fill the sharedConf structure. 
 *
 * @param[in]   fname          #1: Fullname of the qp.shared file
 *
 * @retval      Not NULL      #1: Pointer of the created sharedConf
 * @retval      NULL          #2: Failed to parse the qp.shared file.
 *
 * @note        the file could be json or yaml format.
 *              4 sections in this file: hosttype, hostmodel, resource and cluster.
 */
struct sharedConf *
readconf_shared(char *fname)
{
    quickpool_errno = 0;
    if (NULL == fname) {
        log_message(ERR, NOCLASS, "%s: input file name is null", __func__);
        quickpool_errno = ERROR_CONF_FATAL;
        return NULL;
    }

    qpinfo.num_resources = 0;
    FREEUP(qpinfo.resource_table);
    qpinfo.num_types = 0;
    qpinfo.num_models = 0;
    qpinfo.num_index = 0;
    qpinfo.num_user_index = 0;

    if (NULL == gptrSharedConf) {

        gptrSharedConf = (struct sharedConf *)calloc(1, sizeof(struct sharedConf));
        if (NULL == gptrSharedConf) {

            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sizeof(struct sharedConf));
            quickpool_errno = ERROR_MEMORY;
            return NULL;
        }

        gptrSharedConf->qpinfo = &qpinfo;
        gptrSharedConf->cluster_name = NULL;
        gptrSharedConf->servers = NULL;
    } else {
        FREEUP(gptrSharedConf->cluster_name);
        FREEUP(gptrSharedConf->servers);
        gptrSharedConf->qpinfo = &qpinfo;
    }

    if (initialize_resource_table() < 0 ) {
        quickpool_errno = ERROR_CONF_FATAL;
        return NULL;
    }

    FILE *fp;
    fp = fopen(fname, "r");
    if (fp == NULL) {
        log_message(ERR, NOCLASS, "%s: failed to open file(%s): %m.", __func__, fname);
        quickpool_errno = ERROR_NO_FILE;
        return NULL;
    }

    yaml_parser_t parser;
    if(!yaml_parser_initialize(&parser)) {
        log_message(ERR, NOCLASS, "%s: failed to initialize yaml parser!", __func__);
        quickpool_errno = ERROR_CONF_FATAL;
        fclose(fp);
        return NULL;
    }

    yaml_parser_set_input_file(&parser, fp);

    bool_t modelok=FALSE, resok=FALSE, clsok=FALSE, typeok=FALSE;

    yaml_token_t token;
    char *strKey=NULL;
    do {

        yaml_parser_scan(&parser, &token);

        if (YAML_NO_ERROR != parser.error) {

            log_message(ERR, NOCLASS, "%s: error occurred when reading shared file(%s) at line %d: %s",
                        __func__, fname, parser.mark.line, parser.problem);

            quickpool_errno = ERROR_CONF_FATAL;
            yaml_parser_delete(&parser);
            fclose(fp);

            return NULL;
        }

        switch(token.type) {
        case YAML_SCALAR_TOKEN:

            strKey = (char *)token.data.scalar.value;
            if (NULL == strKey || 0 == strlen(strKey)) {
                continue;
            }

            if (strcasecmp(strKey, "hosttype") == 0) {
                if (parse_hosttype(&parser, fname)) {
                    typeok = TRUE;
                }
            } else if (strcasecmp(strKey, "hostmodel") == 0) {
                if (parse_hostmodel(&parser, fname)) {
                    modelok = TRUE;
                }
            } else if (strcasecmp(strKey, "resource") == 0) {
                if (parse_resource(&parser, fname)) {
                    resok = TRUE;
                }
            } else if (strcasecmp(strKey, "cluster") == 0) {
                if (parse_cluster(&parser, fname)) {
                    clsok = TRUE;
                    break;
                }
            } else {
                log_message(ERR, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                            __func__, fname, parser.mark.line, strKey);
                quickpool_errno = ERROR_CONF_WARNING;
            }
 
           break;
        default:
            break;
        }

        if(token.type != YAML_STREAM_END_TOKEN) {
            yaml_token_delete(&token);
        }
    } while(token.type != YAML_STREAM_END_TOKEN);
    yaml_token_delete(&token);

    yaml_parser_delete(&parser);
    fclose(fp);

    if (!modelok) {
        log_message(ERR, NOCLASS, "%s: HostModel section missing or invalid", __func__);
        quickpool_errno = ERROR_CONF_WARNING;
    }
    if (!resok) {
        log_message(ERR, NOCLASS, "%s: Resource section missing or invalid", __func__);
        quickpool_errno = ERROR_CONF_WARNING;
    }
    if (!typeok) {
        log_message(ERR, NOCLASS, "%s: HostType section missing or invalid", __func__);
        quickpool_errno = ERROR_CONF_WARNING;
    }
    if (!clsok) {
        log_message(ERR, NOCLASS, "%s: Cluster section missing or invalid", __func__);
        quickpool_errno = ERROR_CONF_WARNING;
    }

    return (gptrSharedConf);
} // end function readconf_shared

/**
 * @brief       Read the qp.cluster.name file and fill the clusterConf structure. 
 *
 * @param[in]   fname          #1: Fullname of the qp.cluster.name file
 *
 * @retval      Not NULL      #1: Pointer of the created clusterConf
 * @retval      NULL          #2: Failed to parse the qp.cluster.name file.
 *
 * @note        the file could be json or yaml format.
 *              4 sections in this file: clusteradmins, parameters, host and resourcemap.
 */
struct clusterConf *
readconf_cluster(char *fname)
{
    quickpool_errno = 0;
    if (NULL == fname) {
        log_message(ERR, NOCLASS, "%s: filename is NULL", __func__);
        quickpool_errno = ERROR_NO_FILE;
        return NULL;
    }

    if (NULL == gptrClusterConf) {
        gptrClusterConf = (struct clusterConf *)calloc(1, sizeof(struct clusterConf));
        if (NULL == gptrClusterConf) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sizeof(struct clusterConf));
            quickpool_errno = ERROR_MEMORY;
            return NULL;
        }

        gptrClusterConf->admin = (struct admins *)calloc(1, sizeof(struct admins));
        if (NULL == gptrClusterConf->admin) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sizeof(struct admins));
            quickpool_errno = ERROR_MEMORY;
            return NULL;
        }

#define MAX_NUM_CLUSTER_PARAM  7
        gptrClusterConf->param = (struct config_param *)calloc(MAX_NUM_CLUSTER_PARAM,
                                                               sizeof(struct config_param));
        if (NULL == gptrClusterConf->param) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", MAX_NUM_CLUSTER_PARAM*sizeof(struct config_param));
            quickpool_errno = ERROR_MEMORY;
            return NULL;
        }

        gptrClusterConf->param[0].param_name = "EXINTERVAL";
        gptrClusterConf->param[0].param_type = INT_PARAM;
        gptrClusterConf->param[0].index = -1;

        gptrClusterConf->param[1].param_name = "HOST_INACTIVITY_LIMIT";
        gptrClusterConf->param[1].param_type = INT_PARAM;
        gptrClusterConf->param[1].index = -1;

        gptrClusterConf->param[2].param_name = "MASTER_INACTIVITY_LIMIT";
        gptrClusterConf->param[2].param_type = INT_PARAM;
        gptrClusterConf->param[2].index = -1;

        gptrClusterConf->param[3].param_name = "RETRY_LIMIT";
        gptrClusterConf->param[3].param_type = INT_PARAM;
        gptrClusterConf->param[3].index = -1;

        gptrClusterConf->param[4].param_name = "UNRMARGS";
        gptrClusterConf->param[4].param_type = STRING_PARAM;
        gptrClusterConf->param[4].index = -1;

        gptrClusterConf->param[5].param_name = "UNRM_RESTARTS";
        gptrClusterConf->param[5].param_type = INT_PARAM;
        gptrClusterConf->param[5].index = -1;

        gptrClusterConf->param[6].param_name = NULL;
        gptrClusterConf->param[6].param_type = STRING_PARAM;
        gptrClusterConf->param[6].string_value = NULL;

        sintMaxLocation = 100;
        gptrClusterConf->loc = (struct resourceLoc *)calloc(sintMaxLocation, sizeof(struct resourceLoc));
        if (NULL == gptrClusterConf->admin) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sintMaxLocation*sizeof(struct resourceLoc));
            quickpool_errno = ERROR_MEMORY;
            return NULL;
        }
    }

    FILE *fp;
    if ((fp = fopen(fname, "r")) == NULL) {

        log_message(ERR, NOCLASS, STR_FUNC_S_FAIL, __func__, "fopen", fname);
        quickpool_errno = ERROR_NO_FILE;
        return NULL;
    }

    yaml_parser_t parser;
    if(!yaml_parser_initialize(&parser)) {
        log_message(ERR, NOCLASS, "%s: failed to initialize yaml parser!", __func__);
        quickpool_errno = ERROR_CONF_FATAL;
        fclose(fp);
        return NULL;
    }

    yaml_parser_set_input_file(&parser, fp);

    bool_t errorOccur = FALSE;
    yaml_token_t token;
    char *strKey=NULL;
    do {

        yaml_parser_scan(&parser, &token);

        if (YAML_NO_ERROR != parser.error) {

            log_message(ERR, NOCLASS, "%s: error occurred when reading cluster file(%s) at line %d: %s",
                        __func__, fname, parser.mark.line, parser.problem);

            quickpool_errno = ERROR_CONF_FATAL;
            yaml_parser_delete(&parser);
            fclose(fp);

            return NULL;
        }

        switch(token.type) {
        case YAML_SCALAR_TOKEN:

            strKey = (char *)token.data.scalar.value;
            if (NULL == strKey || 0 == strlen(strKey)) {
                continue;
            }

            if (strcasecmp(strKey, "clusteradmins") == 0) {
                if (!parse_cluster_admin(&parser, fname)) {
                    errorOccur = TRUE;
                }
            } else if (strcasecmp(strKey, "parameters") == 0) {
                if (!parse_cluster_param(&parser, fname)) {
                    errorOccur = TRUE;
                }
            } else if (strcasecmp(strKey, "host") == 0) {
                if (!parse_node(&parser, fname)) {
                    errorOccur = TRUE;
                }
            } else if (strcasecmp(strKey, "resourcemap") == 0) {
                if (!parse_resource_map(&parser, fname)) {
                    errorOccur = TRUE;
                }
            } else {
                log_message(ERR, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                            __func__, fname, parser.mark.line, strKey);
                quickpool_errno = ERROR_CONF_WARNING;
            }
 
            break;
        default:
            break;
        }

        if(token.type != YAML_STREAM_END_TOKEN) {
            yaml_token_delete(&token);
        }
    } while(token.type != YAML_STREAM_END_TOKEN);
    yaml_token_delete(&token);

    yaml_parser_delete(&parser);
    fclose(fp);

    if (gptrClusterConf->admin->num_admins == 0) {
        log_message(ERR, NOCLASS, "%s: not valid cluster administrators definition, root will be set to the only cluster administrator.", __func__);

        struct admins *admin = gptrClusterConf->admin;
        admin->num_admins = 1;

        admin->admin_ids = (uid_t *)calloc(1, sizeof(uid_t));
        admin->admin_gids = (uid_t *)calloc(1, sizeof(uid_t));
        admin->admin_names = (char **)calloc(1, sizeof(char *));
        if (NULL == admin->admin_ids || NULL == admin->admin_gids || NULL == admin->admin_names) {

            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sizeof(uid_t)*2+sizeof(char*));
            quickpool_errno = ERROR_MEMORY;
            return NULL;
        }

        admin->admin_ids[0] = 0;
        admin->admin_gids[0] = 0;
        admin->admin_names[0] = copy_string((char *)"root");
    }

    if (gptrClusterConf->num_hosts > 0) {
        if (errorOccur) {
        }
    } else {

        log_message(ERR, NOCLASS, "%s: There is no hosts defined in cluster file(%s).",
                    __func__, fname);
        quickpool_errno = ERROR_CONF_WARNING;
        return NULL;
    }

    return (gptrClusterConf);
} // end function readconf_cluster

/**
 * @brief       Read the qp.params file and fill the paramConf structure. 
 *
 * @retval      Not NULL      #1: Pointer of the created paramConf
 * @retval      NULL          #2: Failed to parse the qp.params file.
 *
 * @note        the file could be json or yaml format.
 *              1 sections in this file: parameter.
 */
struct QuickPoolParameter *
readconf_parameter(void)
{
    char file[PATH_MAX+1];
    FILE *fp;

    snprintf(file, PATH_MAX, "%s/qp.params", baseParams[BASE_CONFDIR].string_value);
    if ((fp = fopen(file, "r")) == NULL) {

        log_message(ERR, NOCLASS, "%s: failed to read file[%s]. All parameters will be set to default values.",
                    __func__, file);
        quickpool_errno = ERROR_CONF_FATAL;

        return NULL;
    }

    yaml_parser_t parser;
    if(!yaml_parser_initialize(&parser)) {

        log_message(ERR, NOCLASS, "%s: failed to initialize yaml parser!", __func__);
        quickpool_errno = ERROR_CONF_FATAL;
        fclose(fp);

        return NULL;
    }

    yaml_parser_set_input_file(&parser, fp);

    gptrParamConf = (struct QuickPoolParameter *)calloc(1, sizeof(struct QuickPoolParameter));
    if (NULL == gptrParamConf) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct QuickPoolParameter));
        quickpool_errno = ERROR_MEMORY;
        fclose(fp);

        return NULL;
    }

    gptrParamConf->param = (struct config_param *)calloc(PARAM_KEY_MAX+1, sizeof(struct config_param));
    if (NULL == gptrParamConf->param) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", PARAM_KEY_MAX*sizeof(struct config_param));
        quickpool_errno = ERROR_MEMORY;
        fclose(fp);

        return NULL;
    }

    initialize_settings(gptrParamConf->param);
    gptrParamConf->number = PARAM_KEY_MAX;

    quickpool_errno = 0;

    yaml_token_t token;
    bool_t key=FALSE;
    char *strKey=NULL;
    int index = -1;
    do {

        yaml_parser_scan(&parser, &token);

        if (YAML_NO_ERROR != parser.error) {

            log_message(ERR, NOCLASS, "%s: error occurred when reading file(%s) at line %d: %s",
                        __func__, file, parser.mark.line, parser.problem);

            quickpool_errno = ERROR_CONF_FATAL;
            yaml_parser_delete(&parser);
            fclose(fp);

            return NULL;
        }

        switch(token.type) {
        case YAML_KEY_TOKEN:
            key = TRUE;
            break;
        case YAML_VALUE_TOKEN:
            key = FALSE;
            break;
        case YAML_SCALAR_TOKEN:
            if (key) {
                char *strValue = (char *)token.data.scalar.value;

                index = match_param_key(strValue, gptrParamConf->param);
                if (index < 0) {
                    log_message(WARNING, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                                __func__, file, parser.mark.line, strValue);
                    quickpool_errno = ERROR_CONF_WARNING;
                    break;
                }

                strKey = copy_string((char *)token.data.scalar.value);
            } else {
                if (NULL != strKey) {
                    if (index >= 0) {
                        if (!set_param_value(index, (char *)token.data.scalar.value, gptrParamConf->param)) {
                            log_message(ERR, NOCLASS, "%s: error occurred when parsing param section of file(%s) at line %d: see above for reason",
                                __func__, file, parser.mark.line);
                            quickpool_errno = ERROR_CONF_WARNING;
                        }                        
                        index = -1;
                    }

                    FREEUP(strKey);
                }
            }
            break;
        default:
            key = FALSE;
            break;
        }

        if(token.type != YAML_STREAM_END_TOKEN) {
            yaml_token_delete(&token);
        }
    } while(token.type != YAML_STREAM_END_TOKEN);
    yaml_token_delete(&token);

    yaml_parser_delete(&parser);
    fclose(fp);

    /* JS_SLEEP_TIME */
    if (0 <= gptrParamConf->param[PARAM_KEY_JS_SLEEP_TIME].index
        && (gptrParamConf->param[PARAM_KEY_JS_SLEEP_TIME].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_JS_SLEEP_TIME].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_JS_SLEEP_TIME].int_value,
                    gptrParamConf->param[PARAM_KEY_JS_SLEEP_TIME].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_JS_SLEEP_TIME].int_value = DEFAULT_JS_SLEEPTIME;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* JM_SLEEP_TIME */
    if (0 <= gptrParamConf->param[PARAM_KEY_JM_SLEEP_TIME].index
        && (gptrParamConf->param[PARAM_KEY_JM_SLEEP_TIME].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_JM_SLEEP_TIME].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_JM_SLEEP_TIME].int_value,
                    gptrParamConf->param[PARAM_KEY_JM_SLEEP_TIME].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_JM_SLEEP_TIME].int_value = DEFAULT_JM_SLEEPTIME;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* JOB_ACCEPT_INTERVAL */
    if (0 <= gptrParamConf->param[PARAM_KEY_JOB_ACCEPT_INTERVAL].index
        && (gptrParamConf->param[PARAM_KEY_JOB_ACCEPT_INTERVAL].int_value < 0
            || gptrParamConf->param[PARAM_KEY_JOB_ACCEPT_INTERVAL].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 0 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_JOB_ACCEPT_INTERVAL].int_value,
                    gptrParamConf->param[PARAM_KEY_JOB_ACCEPT_INTERVAL].param_name,
                    INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_JOB_ACCEPT_INTERVAL].int_value = DEFAULT_ACCEPT_INTVL;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* MAX_RETRY */
    if (0 <= gptrParamConf->param[PARAM_KEY_MAX_RETRY].index
        && (gptrParamConf->param[PARAM_KEY_MAX_RETRY].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_MAX_RETRY].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_MAX_RETRY].int_value,
                    gptrParamConf->param[PARAM_KEY_MAX_RETRY].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_MAX_RETRY].int_value = DEFAULT_MAX_RETRY;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* MAX_JM_FAIL */
    if (0 <= gptrParamConf->param[PARAM_KEY_MAX_JM_FAIL].index
        && (gptrParamConf->param[PARAM_KEY_MAX_JM_FAIL].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_MAX_JM_FAIL].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_MAX_JM_FAIL].int_value,
                    gptrParamConf->param[PARAM_KEY_MAX_JM_FAIL].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_MAX_JM_FAIL].int_value = DEFAULT_MAXJM_FAIL;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* CLEAN_PERIOD */
    if (0 <= gptrParamConf->param[PARAM_KEY_CLEAN_PERIOD].index
        && (gptrParamConf->param[PARAM_KEY_CLEAN_PERIOD].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_CLEAN_PERIOD].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_CLEAN_PERIOD].int_value,
                    gptrParamConf->param[PARAM_KEY_CLEAN_PERIOD].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_CLEAN_PERIOD].int_value = DEFAULT_CLEAN_PERIOD;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* MAX_JOB_NUM */
    if (0 <= gptrParamConf->param[PARAM_KEY_MAX_JOB_NUM].index
        && (gptrParamConf->param[PARAM_KEY_MAX_JOB_NUM].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_MAX_JOB_NUM].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_MAX_JOB_NUM].int_value,
                    gptrParamConf->param[PARAM_KEY_MAX_JOB_NUM].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_MAX_JOB_NUM].int_value = DEFAULT_MAX_JOB_NUM;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* PG_SUSP_IT */
    if (0 <= gptrParamConf->param[PARAM_KEY_PG_SUSP_IT].index
        && (gptrParamConf->param[PARAM_KEY_PG_SUSP_IT].int_value < 0
            || gptrParamConf->param[PARAM_KEY_PG_SUSP_IT].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 0 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_PG_SUSP_IT].int_value,
                    gptrParamConf->param[PARAM_KEY_PG_SUSP_IT].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_PG_SUSP_IT].int_value = DEFAULT_PG_SUSP_IT;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* RETRY_INTERVAL */
    if (0 <= gptrParamConf->param[PARAM_KEY_RETRY_INTERVAL].index
        && (gptrParamConf->param[PARAM_KEY_RETRY_INTERVAL].int_value < 0
            || gptrParamConf->param[PARAM_KEY_RETRY_INTERVAL].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 0 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_RETRY_INTERVAL].int_value,
                    gptrParamConf->param[PARAM_KEY_RETRY_INTERVAL].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_RETRY_INTERVAL].int_value = DEFAULT_RETRY_INTVL;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* MAX_JM_CONNS */
    if (0 <= gptrParamConf->param[PARAM_KEY_MAX_JM_CONNS].index
        && (gptrParamConf->param[PARAM_KEY_MAX_JM_CONNS].int_value < 0
            || gptrParamConf->param[PARAM_KEY_MAX_JM_CONNS].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 0 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_MAX_JM_CONNS].int_value,
                    gptrParamConf->param[PARAM_KEY_MAX_JM_CONNS].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_MAX_JM_CONNS].int_value = DEFAULT_MAX_JM_CONNS;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* RUSAGE_UPDATE_RATE */
    if (0 <= gptrParamConf->param[PARAM_KEY_RUSAGE_UPDATE_RATE].index
        && (gptrParamConf->param[PARAM_KEY_RUSAGE_UPDATE_RATE].int_value < 0
            || gptrParamConf->param[PARAM_KEY_RUSAGE_UPDATE_RATE].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 0 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_RUSAGE_UPDATE_RATE].int_value,
                    gptrParamConf->param[PARAM_KEY_RUSAGE_UPDATE_RATE].param_name,
                    INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_RUSAGE_UPDATE_RATE].int_value = DEFAULT_RUSAGE_UPDATE_RATE;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* RUSAGE_UPDATE_PERCENT */
    if (0 <= gptrParamConf->param[PARAM_KEY_RUSAGE_UPDATE_PERCENT].index
        && (gptrParamConf->param[PARAM_KEY_RUSAGE_UPDATE_PERCENT].int_value < 0
            || gptrParamConf->param[PARAM_KEY_RUSAGE_UPDATE_PERCENT].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 0 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_RUSAGE_UPDATE_PERCENT].int_value,
                    gptrParamConf->param[PARAM_KEY_RUSAGE_UPDATE_PERCENT].param_name,
                    INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_RUSAGE_UPDATE_PERCENT].int_value = DEFAULT_RUSAGE_UPDATE_PERCENT;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* MAX_SCHEDULE_TIME */
    if (0 <= gptrParamConf->param[PARAM_KEY_MAX_SCHEDULE_TIME].index
        && (gptrParamConf->param[PARAM_KEY_MAX_SCHEDULE_TIME].int_value < 0
            || gptrParamConf->param[PARAM_KEY_MAX_SCHEDULE_TIME].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 0 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_MAX_SCHEDULE_TIME].int_value,
                    gptrParamConf->param[PARAM_KEY_MAX_SCHEDULE_TIME].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_MAX_SCHEDULE_TIME].int_value = DEFAULT_SCHEDULE_TIME;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* LOAD_UPDATE_INTERVAL */
    if (0 <= gptrParamConf->param[PARAM_KEY_LOAD_UPDATE_INTERVAL].index
        && (gptrParamConf->param[PARAM_KEY_LOAD_UPDATE_INTERVAL].int_value < 0
            || gptrParamConf->param[PARAM_KEY_LOAD_UPDATE_INTERVAL].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 0 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_LOAD_UPDATE_INTERVAL].int_value,
                    gptrParamConf->param[PARAM_KEY_LOAD_UPDATE_INTERVAL].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_LOAD_UPDATE_INTERVAL].int_value = DEFAULT_LOAD_UPDATE_INTERVAL;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* MAX_JOBID */
    if (0 <= gptrParamConf->param[PARAM_KEY_MAX_JOBID].index
        && (gptrParamConf->param[PARAM_KEY_MAX_JOBID].int_value < MAX_JOBID_LOW
            || gptrParamConf->param[PARAM_KEY_MAX_JOBID].int_value > MAX_JOBID_HIGH)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s is not in [%d, %d], use default value %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_MAX_JOBID].int_value,
                    gptrParamConf->param[PARAM_KEY_MAX_JOBID].param_name,
                    MAX_JOBID_LOW, MAX_JOBID_HIGH, DEFAULT_MAX_JOBID);

        gptrParamConf->param[PARAM_KEY_MAX_JOBID].int_value = DEFAULT_MAX_JOBID;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* MAX_JOB_ARRAY_SIZE */
    if (0 <= gptrParamConf->param[PARAM_KEY_MAX_JOB_ARRAY_SIZE].index
        && (gptrParamConf->param[PARAM_KEY_MAX_JOB_ARRAY_SIZE].int_value < 1
            || gptrParamConf->param[PARAM_KEY_MAX_JOB_ARRAY_SIZE].int_value >= MAXARRAYINDEX)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s is out of range[1-%d]).",
                    __func__, file, gptrParamConf->param[PARAM_KEY_MAX_JOB_ARRAY_SIZE].int_value,
                    gptrParamConf->param[PARAM_KEY_MAX_JOB_ARRAY_SIZE].param_name, MAXARRAYINDEX);

        gptrParamConf->param[PARAM_KEY_MAX_JOB_ARRAY_SIZE].int_value = DEFAULT_MAX_JOBARRAY_SIZE;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* JOB_TERMINATE_INTERVAL */
    if (0 <= gptrParamConf->param[PARAM_KEY_JOB_TERMINATE_INTERVAL].index
        && (gptrParamConf->param[PARAM_KEY_JOB_TERMINATE_INTERVAL].int_value < 0
            || gptrParamConf->param[PARAM_KEY_JOB_TERMINATE_INTERVAL].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 0 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_JOB_TERMINATE_INTERVAL].int_value,
                    gptrParamConf->param[PARAM_KEY_JOB_TERMINATE_INTERVAL].param_name,
                    INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_JOB_TERMINATE_INTERVAL].int_value = DEFAULT_JOB_TERM_INTERVAL;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* JOB_DEP_LAST_SUB */

    /* MAX_USER_PRIORITY */
    if (0 <= gptrParamConf->param[PARAM_KEY_MAX_USER_PRIORITY].index
        && (gptrParamConf->param[PARAM_KEY_MAX_USER_PRIORITY].int_value < 0
            || gptrParamConf->param[PARAM_KEY_MAX_USER_PRIORITY].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 0 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_MAX_USER_PRIORITY].int_value,
                    gptrParamConf->param[PARAM_KEY_MAX_USER_PRIORITY].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_MAX_USER_PRIORITY].int_value = -1;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* JOB_PRIORITY_OVER_TIME */
    if (0 <= gptrParamConf->param[PARAM_KEY_JOB_PRIORITY_OVER_TIME].index) {
        int mytime = 0, value = 0;
        char *sp, *ptr;

        sp = gptrParamConf->param[PARAM_KEY_JOB_PRIORITY_OVER_TIME].string_value;
        ptr = strchr(sp, '/');
        if (ptr != NULL) {
            *ptr = 0;
            ptr++;
            value = gf_atoi(sp, INFINIT_INT, 0);
            mytime= gf_atoi(ptr, INFINIT_INT, 0);

            --ptr;
            *ptr = '/';
        }

        if (ptr == NULL || value == INFINIT_INT || mytime == INFINIT_INT) {
            log_message(ERR, NOCLASS, "%s: In %s, value <%s> of %s isn't in format value/time (time in minutes) or value is not positive.",
                        __func__, gptrParamConf->param[PARAM_KEY_JOB_PRIORITY_OVER_TIME].string_value,
                        gptrParamConf->param[PARAM_KEY_JOB_PRIORITY_OVER_TIME].param_name);

            quickpool_errno = ERROR_CONF_WARNING;
        } else {

            gptrParamConf->param[PARAM_KEY_JOB_PRIORITY_VALUE].int_value = value;
            gptrParamConf->param[PARAM_KEY_JOB_PRIORITY_TIME].int_value = mytime;
        }
    }

    /* RESOURCE_UPDATE_INTERVAL */
    if (0 <= gptrParamConf->param[PARAM_KEY_RESOURCE_UPDATE_INTERVAL].index
        && (gptrParamConf->param[PARAM_KEY_RESOURCE_UPDATE_INTERVAL].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_RESOURCE_UPDATE_INTERVAL].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_RESOURCE_UPDATE_INTERVAL].int_value,
                    gptrParamConf->param[PARAM_KEY_RESOURCE_UPDATE_INTERVAL].param_name,
                    INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_RESOURCE_UPDATE_INTERVAL].int_value = INFINIT_INT;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* PRE_EXEC_DELAY */
    if (0 <= gptrParamConf->param[PARAM_KEY_PRE_EXEC_DELAY].index
        && (gptrParamConf->param[PARAM_KEY_PRE_EXEC_DELAY].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_PRE_EXEC_DELAY].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_PRE_EXEC_DELAY].int_value,
                    gptrParamConf->param[PARAM_KEY_PRE_EXEC_DELAY].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_PRE_EXEC_DELAY].int_value = DEFAULT_PRE_EXEC_DELAY;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* RESOURCE_RESERVE */

    /* MAX_ACCT_ARCHIVE_FILE */
    if (0 <= gptrParamConf->param[PARAM_KEY_MAX_ACCT_ARCHIVE_FILE].index
        && (gptrParamConf->param[PARAM_KEY_MAX_ACCT_ARCHIVE_FILE].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_MAX_ACCT_ARCHIVE_FILE].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_MAX_ACCT_ARCHIVE_FILE].int_value,
                    gptrParamConf->param[PARAM_KEY_MAX_ACCT_ARCHIVE_FILE].param_name,
                    INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_MAX_ACCT_ARCHIVE_FILE].int_value = -1;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* ACCT_ARCHIVE_SIZE */
    if (0 <= gptrParamConf->param[PARAM_KEY_ACCT_ARCHIVE_SIZE].index
        && (gptrParamConf->param[PARAM_KEY_ACCT_ARCHIVE_SIZE].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_ACCT_ARCHIVE_SIZE].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_ACCT_ARCHIVE_SIZE].int_value,
                    gptrParamConf->param[PARAM_KEY_ACCT_ARCHIVE_SIZE].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_ACCT_ARCHIVE_SIZE].int_value = -1;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* ACCT_ARCHIVE_AGE */
    if (0 <= gptrParamConf->param[PARAM_KEY_ACCT_ARCHIVE_AGE].index
        && (gptrParamConf->param[PARAM_KEY_ACCT_ARCHIVE_AGE].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_ACCT_ARCHIVE_AGE].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_ACCT_ARCHIVE_AGE].int_value,
                    gptrParamConf->param[PARAM_KEY_ACCT_ARCHIVE_AGE].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_ACCT_ARCHIVE_AGE].int_value = -1;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* MAX_PREEMPT_JOBS */
    if (0 <= gptrParamConf->param[PARAM_KEY_MAX_PREEMPT_JOBS].index
        && (gptrParamConf->param[PARAM_KEY_MAX_PREEMPT_JOBS].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_MAX_PREEMPT_JOBS].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_MAX_PREEMPT_JOBS].int_value,
                    gptrParamConf->param[PARAM_KEY_MAX_PREEMPT_JOBS].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_MAX_PREEMPT_JOBS].int_value = DEFAULT_MAX_PREEMPT_JOBS;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* MAX_NUM_CANDIDATES */
    if (0 <= gptrParamConf->param[PARAM_KEY_MAX_NUM_CANDIDATES].index
        && (gptrParamConf->param[PARAM_KEY_MAX_NUM_CANDIDATES].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_MAX_NUM_CANDIDATES].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_MAX_NUM_CANDIDATES].int_value,
                    gptrParamConf->param[PARAM_KEY_MAX_NUM_CANDIDATES].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_MAX_NUM_CANDIDATES].int_value = INFINIT_INT;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    /* ENABLE_PROXY_HOSTS */

    /* DISABLE_PEER_JOBS */
    /* ENABLE_PTILE_PACK */

    /* MAX_JOB_SCHEDULE */
    if (0 <= gptrParamConf->param[PARAM_KEY_MAX_JOB_SCHEDULE].index
        && (gptrParamConf->param[PARAM_KEY_MAX_JOB_SCHEDULE].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_MAX_JOB_SCHEDULE].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_MAX_JOB_SCHEDULE].int_value,
                    gptrParamConf->param[PARAM_KEY_MAX_JOB_SCHEDULE].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_MAX_JOB_SCHEDULE].int_value = INFINIT_INT;
        quickpool_errno = ERROR_CONF_WARNING;   
    }

    /* HJOB_PER_SESSION */
    if (0 <= gptrParamConf->param[PARAM_KEY_HJOB_PER_SESSION].index
        && (gptrParamConf->param[PARAM_KEY_HJOB_PER_SESSION].int_value <= 0
            || gptrParamConf->param[PARAM_KEY_HJOB_PER_SESSION].int_value >= INFINIT_INT)) {

        log_message(ERR, NOCLASS, "%s: In %s, value <%d> of %s isn't an integer between 1 and %d.",
                    __func__, file, gptrParamConf->param[PARAM_KEY_HJOB_PER_SESSION].int_value,
                    gptrParamConf->param[PARAM_KEY_HJOB_PER_SESSION].param_name, INFINIT_INT - 1);

        gptrParamConf->param[PARAM_KEY_HJOB_PER_SESSION].int_value = DEFAULT_HJOB_PER_SESSION;
        quickpool_errno = ERROR_CONF_WARNING;   
    }

    /* RECORD_SWITCH_NOFORK */

    /* MIGRATE_TOPEND */

    /* NO_QSORT_JOBS */

    /* MODIFY_RUNNING_JOB */

    /* PREEMPT_FOR_REQUEUE */

    if (gptrParamConf->param[PARAM_KEY_MAX_USER_PRIORITY].int_value <= 0
        && (gptrParamConf->param[PARAM_KEY_JOB_PRIORITY_VALUE].int_value > 0
            || gptrParamConf->param[PARAM_KEY_JOB_PRIORITY_TIME].int_value > 0)) {
        log_message(ERR, NOCLASS, "%s: MAX_USER_PRIORITY must be defined to use JOB_PRIORITY_OVER_TIME: job priority control disabled",
                    __func__);
        gptrParamConf->param[PARAM_KEY_JOB_PRIORITY_VALUE].int_value = -1;
        gptrParamConf->param[PARAM_KEY_JOB_PRIORITY_TIME].int_value  = -1;
        quickpool_errno = ERROR_CONF_WARNING;
    }

    return gptrParamConf;
} // end function readconf_parameter

/**
 * @brief       Initialize parameters. 
 *
 * @param[in|out]   param     #1: parameter
 *
 */
void
initialize_settings(struct config_param *param)
{
    if (NULL == param) {
        return;
    }

    param[PARAM_KEY_DEFAULT_QUEUE].param_name = "DEFAULT_QUEUE";
    param[PARAM_KEY_DEFAULT_QUEUE].param_type = STRING_PARAM;
    param[PARAM_KEY_DEFAULT_QUEUE].index = -1;

    param[PARAM_KEY_DEFAULT_PROJECT].param_name = "DEFAULT_PROJECT";
    param[PARAM_KEY_DEFAULT_PROJECT].param_type = STRING_PARAM;
    param[PARAM_KEY_DEFAULT_PROJECT].index = -1;

    param[PARAM_KEY_JS_SLEEP_TIME].param_name = "JS_SLEEP_TIME";
    param[PARAM_KEY_JS_SLEEP_TIME].param_type = INT_PARAM;
    param[PARAM_KEY_JS_SLEEP_TIME].index = -1;
    param[PARAM_KEY_JS_SLEEP_TIME].int_value = DEFAULT_JS_SLEEPTIME;

    param[PARAM_KEY_JM_SLEEP_TIME].param_name = "JM_SLEEP_TIME";
    param[PARAM_KEY_JM_SLEEP_TIME].param_type = INT_PARAM;
    param[PARAM_KEY_JM_SLEEP_TIME].index = -1;
    param[PARAM_KEY_JM_SLEEP_TIME].int_value = DEFAULT_JM_SLEEPTIME;
   
    param[PARAM_KEY_JOB_ACCEPT_INTERVAL].param_name = "JOB_ACCEPT_INTERVAL";
    param[PARAM_KEY_JOB_ACCEPT_INTERVAL].param_type = INT_PARAM;
    param[PARAM_KEY_JOB_ACCEPT_INTERVAL].index = -1;
    param[PARAM_KEY_JOB_ACCEPT_INTERVAL].int_value = DEFAULT_ACCEPT_INTVL;

    param[PARAM_KEY_MAX_RETRY].param_name = "MAX_RETRY";
    param[PARAM_KEY_MAX_RETRY].param_type = INT_PARAM;
    param[PARAM_KEY_MAX_RETRY].index = -1;
    param[PARAM_KEY_MAX_RETRY].int_value = DEFAULT_MAX_RETRY;

    param[PARAM_KEY_MAX_JM_FAIL].param_name = "MAX_JM_FAIL";
    param[PARAM_KEY_MAX_JM_FAIL].param_type = INT_PARAM;
    param[PARAM_KEY_MAX_JM_FAIL].index = -1;
    param[PARAM_KEY_MAX_JM_FAIL].int_value = DEFAULT_MAXJM_FAIL;

    param[PARAM_KEY_CLEAN_PERIOD].param_name = "CLEAN_PERIOD";
    param[PARAM_KEY_CLEAN_PERIOD].param_type = INT_PARAM;
    param[PARAM_KEY_CLEAN_PERIOD].index = -1;
    param[PARAM_KEY_CLEAN_PERIOD].int_value = DEFAULT_CLEAN_PERIOD;

    param[PARAM_KEY_MAX_JOB_NUM].param_name = "MAX_JOB_NUM";
    param[PARAM_KEY_MAX_JOB_NUM].param_type = INT_PARAM;
    param[PARAM_KEY_MAX_JOB_NUM].index = -1;
    param[PARAM_KEY_MAX_JOB_NUM].int_value = DEFAULT_MAX_JOB_NUM;

    param[PARAM_KEY_PG_SUSP_IT].param_name = "PG_SUSP_IT";
    param[PARAM_KEY_PG_SUSP_IT].param_type = INT_PARAM;
    param[PARAM_KEY_PG_SUSP_IT].index = -1;
    param[PARAM_KEY_PG_SUSP_IT].int_value = DEFAULT_PG_SUSP_IT;

    param[PARAM_KEY_RETRY_INTERVAL].param_name = "RETRY_INTERVAL";
    param[PARAM_KEY_RETRY_INTERVAL].param_type = INT_PARAM;
    param[PARAM_KEY_RETRY_INTERVAL].index = -1;
    param[PARAM_KEY_RETRY_INTERVAL].int_value = DEFAULT_RETRY_INTVL;

    param[PARAM_KEY_MAX_JM_CONNS].param_name = "MAX_JM_CONNS";
    param[PARAM_KEY_MAX_JM_CONNS].param_type = INT_PARAM;
    param[PARAM_KEY_MAX_JM_CONNS].index = -1;
    param[PARAM_KEY_MAX_JM_CONNS].int_value = DEFAULT_MAX_JM_CONNS;

    param[PARAM_KEY_RUSAGE_UPDATE_RATE].param_name = "RUSAGE_UPDATE_RATE";
    param[PARAM_KEY_RUSAGE_UPDATE_RATE].param_type = INT_PARAM;
    param[PARAM_KEY_RUSAGE_UPDATE_RATE].index = -1;
    param[PARAM_KEY_RUSAGE_UPDATE_RATE].int_value = DEFAULT_RUSAGE_UPDATE_RATE;

    param[PARAM_KEY_RUSAGE_UPDATE_PERCENT].param_name = "RUSAGE_UPDATE_PERCENT";
    param[PARAM_KEY_RUSAGE_UPDATE_PERCENT].param_type = INT_PARAM;
    param[PARAM_KEY_RUSAGE_UPDATE_PERCENT].index = -1;
    param[PARAM_KEY_RUSAGE_UPDATE_PERCENT].int_value = DEFAULT_RUSAGE_UPDATE_PERCENT;

    param[PARAM_KEY_MAX_SCHEDULE_TIME].param_name = "MAX_SCHEDULE_TIME";
    param[PARAM_KEY_MAX_SCHEDULE_TIME].param_type = INT_PARAM;
    param[PARAM_KEY_MAX_SCHEDULE_TIME].index = -1;
    param[PARAM_KEY_MAX_SCHEDULE_TIME].int_value = DEFAULT_SCHEDULE_TIME;

    param[PARAM_KEY_LOAD_UPDATE_INTERVAL].param_name = "LOAD_UPDATE_INTERVAL";
    param[PARAM_KEY_LOAD_UPDATE_INTERVAL].param_type = INT_PARAM;
    param[PARAM_KEY_LOAD_UPDATE_INTERVAL].index = -1;
    param[PARAM_KEY_LOAD_UPDATE_INTERVAL].int_value = DEFAULT_LOAD_UPDATE_INTERVAL;

    param[PARAM_KEY_MAX_JOBID].param_name = "MAX_JOBID";
    param[PARAM_KEY_MAX_JOBID].param_type = INT_PARAM;
    param[PARAM_KEY_MAX_JOBID].index = -1;
    param[PARAM_KEY_MAX_JOBID].int_value = DEFAULT_MAX_JOBID;

    param[PARAM_KEY_MAX_JOB_ARRAY_SIZE].param_name = "MAX_JOB_ARRAY_SIZE";
    param[PARAM_KEY_MAX_JOB_ARRAY_SIZE].param_type = INT_PARAM;
    param[PARAM_KEY_MAX_JOB_ARRAY_SIZE].index = -1;
    param[PARAM_KEY_MAX_JOB_ARRAY_SIZE].int_value = DEFAULT_MAX_JOBARRAY_SIZE;

    param[PARAM_KEY_JOB_TERMINATE_INTERVAL].param_name = "JOB_TERMINATE_INTERVAL";
    param[PARAM_KEY_JOB_TERMINATE_INTERVAL].param_type = INT_PARAM;
    param[PARAM_KEY_JOB_TERMINATE_INTERVAL].index = -1;
    param[PARAM_KEY_JOB_TERMINATE_INTERVAL].int_value = DEFAULT_JOB_TERM_INTERVAL;

    param[PARAM_KEY_JOB_DEP_LAST_SUB].param_name = "JOB_DEP_LAST_SUB";
    param[PARAM_KEY_JOB_DEP_LAST_SUB].param_type = BOOL_PARAM;
    param[PARAM_KEY_JOB_DEP_LAST_SUB].index = -1;

    param[PARAM_KEY_MAX_USER_PRIORITY].param_name = "MAX_USER_PRIORITY";
    param[PARAM_KEY_MAX_USER_PRIORITY].param_type = INT_PARAM;
    param[PARAM_KEY_MAX_USER_PRIORITY].index = -1;
    param[PARAM_KEY_MAX_USER_PRIORITY].int_value = -1;

    param[PARAM_KEY_JOB_PRIORITY_OVER_TIME].param_name = "JOB_PRIORITY_OVER_TIME";
    param[PARAM_KEY_JOB_PRIORITY_OVER_TIME].param_type = STRING_PARAM;
    param[PARAM_KEY_JOB_PRIORITY_OVER_TIME].index = -1;

    param[PARAM_KEY_JOB_PRIORITY_VALUE].param_name = "JOB_PRIORITY_VALUE";
    param[PARAM_KEY_JOB_PRIORITY_VALUE].param_type = INT_PARAM;
    param[PARAM_KEY_JOB_PRIORITY_VALUE].index = -1;
    param[PARAM_KEY_JOB_PRIORITY_VALUE].int_value = -1;

    param[PARAM_KEY_JOB_PRIORITY_TIME].param_name = "JOB_PRIORITY_TIME";
    param[PARAM_KEY_JOB_PRIORITY_TIME].param_type = INT_PARAM;
    param[PARAM_KEY_JOB_PRIORITY_TIME].index = -1;
    param[PARAM_KEY_JOB_PRIORITY_TIME].int_value = -1;

    param[PARAM_KEY_RESOURCE_UPDATE_INTERVAL].param_name = "RESOURCE_UPDATE_INTERVAL";
    param[PARAM_KEY_RESOURCE_UPDATE_INTERVAL].param_type = INT_PARAM;
    param[PARAM_KEY_RESOURCE_UPDATE_INTERVAL].index = -1;
    param[PARAM_KEY_RESOURCE_UPDATE_INTERVAL].int_value = DEFAULT_LOAD_UPDATE_INTERVAL;

    param[PARAM_KEY_PRE_EXEC_DELAY].param_name = "PRE_EXEC_DELAY";
    param[PARAM_KEY_PRE_EXEC_DELAY].param_type = INT_PARAM;
    param[PARAM_KEY_PRE_EXEC_DELAY].index = -1;
    param[PARAM_KEY_PRE_EXEC_DELAY].int_value = DEFAULT_PRE_EXEC_DELAY;

    param[PARAM_KEY_RESOURCE_RESERVE].param_name = "RESOURCE_RESERVE";
    param[PARAM_KEY_RESOURCE_RESERVE].param_type = BOOL_PARAM;
    param[PARAM_KEY_RESOURCE_RESERVE].index = -1;

    param[PARAM_KEY_MAX_ACCT_ARCHIVE_FILE].param_name = "MAX_ACCT_ARCHIVE_FILE";
    param[PARAM_KEY_MAX_ACCT_ARCHIVE_FILE].param_type = INT_PARAM;
    param[PARAM_KEY_MAX_ACCT_ARCHIVE_FILE].index = -1;
    param[PARAM_KEY_MAX_ACCT_ARCHIVE_FILE].int_value = -1;

    param[PARAM_KEY_ACCT_ARCHIVE_SIZE].param_name = "ACCT_ARCHIVE_SIZE";
    param[PARAM_KEY_ACCT_ARCHIVE_SIZE].param_type = INT_PARAM;
    param[PARAM_KEY_ACCT_ARCHIVE_SIZE].index = -1;
    param[PARAM_KEY_ACCT_ARCHIVE_SIZE].int_value = -1;

    param[PARAM_KEY_ACCT_ARCHIVE_AGE].param_name = "ACCT_ARCHIVE_AGE";
    param[PARAM_KEY_ACCT_ARCHIVE_AGE].param_type = INT_PARAM;
    param[PARAM_KEY_ACCT_ARCHIVE_AGE].index = -1;
    param[PARAM_KEY_ACCT_ARCHIVE_AGE].int_value = -1;

    param[PARAM_KEY_MAX_PREEMPT_JOBS].param_name = "MAX_PREEMPT_JOBS";
    param[PARAM_KEY_MAX_PREEMPT_JOBS].param_type = INT_PARAM;
    param[PARAM_KEY_MAX_PREEMPT_JOBS].index = -1;
    param[PARAM_KEY_MAX_PREEMPT_JOBS].int_value = DEFAULT_MAX_PREEMPT_JOBS;

    param[PARAM_KEY_MAX_NUM_CANDIDATES].param_name = "MAX_NUM_CANDIDATES";
    param[PARAM_KEY_MAX_NUM_CANDIDATES].param_type = INT_PARAM;
    param[PARAM_KEY_MAX_NUM_CANDIDATES].index = -1;
    param[PARAM_KEY_MAX_NUM_CANDIDATES].int_value = INFINIT_INT;

    param[PARAM_KEY_ENABLE_PROXY_HOSTS].param_name = "ENABLE_PROXY_HOSTS";
    param[PARAM_KEY_ENABLE_PROXY_HOSTS].param_type = BOOL_PARAM;
    param[PARAM_KEY_ENABLE_PROXY_HOSTS].index = -1;

    param[PARAM_KEY_DISABLE_PEER_JOBS].param_name = "DISABLE_PEER_JOBS";
    param[PARAM_KEY_DISABLE_PEER_JOBS].param_type = BOOL_PARAM;
    param[PARAM_KEY_DISABLE_PEER_JOBS].index = -1;

    param[PARAM_KEY_ENABLE_PTILE_PACK].param_name = "ENABLE_PTILE_PACK";
    param[PARAM_KEY_ENABLE_PTILE_PACK].param_type = BOOL_PARAM;
    param[PARAM_KEY_ENABLE_PTILE_PACK].index = -1;

    param[PARAM_KEY_MAX_JOB_SCHEDULE].param_name = "MAX_JOB_SCHEDULE";
    param[PARAM_KEY_MAX_JOB_SCHEDULE].param_type = INT_PARAM;
    param[PARAM_KEY_MAX_JOB_SCHEDULE].index = -1;
    param[PARAM_KEY_MAX_JOB_SCHEDULE].int_value = INFINIT_INT;

    param[PARAM_KEY_HJOB_PER_SESSION].param_name = "HJOB_PER_SESSION";
    param[PARAM_KEY_HJOB_PER_SESSION].param_type = INT_PARAM;
    param[PARAM_KEY_HJOB_PER_SESSION].index = -1;
    param[PARAM_KEY_HJOB_PER_SESSION].int_value = DEFAULT_HJOB_PER_SESSION;

    param[PARAM_KEY_RECORD_SWITCH_NOFORK].param_name = "RECORD_SWITCH_NOFORK";
    param[PARAM_KEY_RECORD_SWITCH_NOFORK].param_type = BOOL_PARAM;
    param[PARAM_KEY_RECORD_SWITCH_NOFORK].index = -1;

    param[PARAM_KEY_MIGRATE_TOPEND].param_name = "MIGRATE_TOPEND";
    param[PARAM_KEY_MIGRATE_TOPEND].param_type = BOOL_PARAM;
    param[PARAM_KEY_MIGRATE_TOPEND].index = -1;

    param[PARAM_KEY_NO_QSORT_JOBS].param_name = "NO_QSORT_JOBS";
    param[PARAM_KEY_NO_QSORT_JOBS].param_type = BOOL_PARAM;
    param[PARAM_KEY_NO_QSORT_JOBS].index = -1;

    param[PARAM_KEY_MODIFY_RUNNING_JOB].param_name = "MODIFY_RUNNING_JOB";
    param[PARAM_KEY_MODIFY_RUNNING_JOB].param_type = BOOL_PARAM;
    param[PARAM_KEY_MODIFY_RUNNING_JOB].index = -1;

    param[PARAM_KEY_PREEMPT_FOR_REQUEUE].param_name = "PREEMPT_FOR_REQUEUE";
    param[PARAM_KEY_PREEMPT_FOR_REQUEUE].param_type = BOOL_PARAM;
    param[PARAM_KEY_PREEMPT_FOR_REQUEUE].index = -1;
    param[PARAM_KEY_PREEMPT_FOR_REQUEUE].bool_value = TRUE;

    param[PARAM_KEY_MAX_JOB_REQUEUE].param_name = "MAX_JOB_REQUEUE";
    param[PARAM_KEY_MAX_JOB_REQUEUE].param_type = INT_PARAM;
    param[PARAM_KEY_MAX_JOB_REQUEUE].index = -1;
    param[PARAM_KEY_MAX_JOB_REQUEUE].int_value = INFINIT_INT;

    param[PARAM_KEY_ALLOW_ROOT_SUBMIT].param_name = "ALLOW_ROOT_SUBMIT";
    param[PARAM_KEY_ALLOW_ROOT_SUBMIT].param_type = BOOL_PARAM;
    param[PARAM_KEY_ALLOW_ROOT_SUBMIT].index = -1;

    param[PARAM_KEY_ENABLE_EXCLUSIVE].param_name = "ENABLE_EXCLUSIVE";
    param[PARAM_KEY_ENABLE_EXCLUSIVE].param_type = BOOL_PARAM;
    param[PARAM_KEY_ENABLE_EXCLUSIVE].index = -1;

    param[PARAM_KEY_MAX].param_name = NULL;
    param[PARAM_KEY_MAX].param_type = STRING_PARAM;

    return;
} // end function initialize_settings

/**
 * @brief       Initialize treshold for load scheduling and load stop. 
 *
 * @param[in]   num        #1: total number of load index
 * @param[in]   restable   #2: cluster resource table
 * @param[out]  loadsched  #3: load schedule array to be initialized
 * @param[out]  loadstop   #4: load stop array to be initialized
 *
 */
void
initialize_threshold(unsigned int num, struct resource_definition *restable,
                     float loadsched[], float loadstop[])
{
    unsigned int i;
    for (i=0; i<num; i++) {

        if (INCR == restable[i].order_type) {
            loadsched[i] = INFINIT_LOAD;
            loadstop[i]  = INFINIT_LOAD;
        } else {
            loadsched[i] = -INFINIT_LOAD;
            loadstop[i]  = -INFINIT_LOAD;
        }
    }

    return;
} // end function initialize_threshold

/**
 * @brief       Read the qp.hosts file and fill the hostConf structure. 
 *
 * @param[in]   info          #1: QuickPool information
 * @param[in]   conf          #2: QuickPool cluster configuration
 *
 * @retval      Not NULL      #1: Pointer of the created hostConf
 * @retval      NULL          #2: Failed to parse the qp.hosts file.
 *
 * @note        the file could be json or yaml format.
 *              2 sections in this file: host, hostgroup .
 */
struct hostConf *
readconf_host(struct quickpoolInfo *info, struct clusterConf *conf)
{
    quickpool_errno = 0;
    if (NULL == info) {
        log_message(ERR, NOCLASS, NULL_POINTER_STR, __func__,  "qp_info");
        quickpool_errno = ERROR_CONF_FATAL;
        return NULL;
    }

    if (NULL == conf) {
        log_message(ERR, NOCLASS, NULL_POINTER_STR, __func__, "clusterConf");
        quickpool_errno = ERROR_CONF_FATAL;
        return NULL;
    }

    gptrClusterConf = conf;

    if (create_hostconf()) {
        quickpool_errno = ERROR_CONF_FATAL;
        return NULL;
    }

    sptrHostConf->num_hosts = gptrClusterConf->num_hosts;
    sptrHostConf->hosts = gptrClusterConf->hosts;

    shashHost = gf_hash_make(gptrClusterConf->num_hosts);
    if (NULL == shashHost) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "gf_hash_make", gptrClusterConf->num_hosts);
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    unsigned int i;
    for (i=0; i<sptrHostConf->num_hosts; i++) {
        gf_hash_install(shashHost, sptrHostConf->hosts[i].host, (void *)&(sptrHostConf->hosts[i]));
        initialize_hostlimit(&(sptrHostConf->hosts[i]));
    }

    char file[PATH_MAX+1];
    FILE *fp;

    snprintf(file, PATH_MAX, "%s/qp.hosts", baseParams[BASE_CONFDIR].string_value);
    if ((fp = fopen(file, "r")) == NULL) {

        log_message(ERR, NOCLASS, "%s: failed to read file[%s]. All hosts known by cluster will be used.",
                    __func__, file);
        quickpool_errno = ERROR_CONF_FATAL;

        return NULL;
    }

    yaml_parser_t parser;
    if(!yaml_parser_initialize(&parser)) {

        log_message(ERR, NOCLASS, "%s: failed to initialize yaml parser!", __func__);
        quickpool_errno = ERROR_CONF_FATAL;
        fclose(fp);

        return NULL;
    }

    yaml_parser_set_input_file(&parser, fp);

    bool_t hostok = FALSE;
    yaml_token_t token;
    char *strKey=NULL;
    do {

        yaml_parser_scan(&parser, &token);

        if (YAML_NO_ERROR != parser.error) {

            log_message(ERR, NOCLASS, "%s: error occurred when reading host file(%s) at line %d: %s",
                        __func__, file, parser.mark.line, parser.problem);

            quickpool_errno = ERROR_CONF_FATAL;
            yaml_parser_delete(&parser);
            fclose(fp);

            return NULL;
        }

        switch(token.type) {
        case YAML_SCALAR_TOKEN:

            strKey = (char *)token.data.scalar.value;
            if (NULL == strKey || 0 == strlen(strKey)) {
                continue;
            }

            if (strcasecmp(strKey, "host") == 0) {
                if (parse_jsnode(&parser, file, info)) {
                    hostok = TRUE;
                }
            } else if (strcasecmp(strKey, "hostgroup") == 0) {

                parse_host_group(&parser, file); 
            } else {
                log_message(ERR, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                            __func__, file, parser.mark.line, strKey);
                quickpool_errno = ERROR_CONF_WARNING;
            }
 
           break;
        default:
            break;
        }

        if(token.type != YAML_STREAM_END_TOKEN) {
            yaml_token_delete(&token);
        }
    } while(token.type != YAML_STREAM_END_TOKEN);
    yaml_token_delete(&token);

    yaml_parser_delete(&parser);
    fclose(fp);

    if (quickpool_errno == ERROR_MEMORY) {

        quickpool_errno = ERROR_CONF_FATAL;
        return NULL;
    }

    if (!hostok) {
        log_message(ERR, NOCLASS, "%s: host section missing.", __func__);
        quickpool_errno = ERROR_CONF_WARNING;
    }

    if (NULL != sptrDefaultHost) {
        for (i=0; i<sptrHostConf->num_hosts; i++) {
            if (INFINIT_INT == sptrHostConf->hosts[i].mig && INFINIT_INT != sptrDefaultHost->mig) {
                sptrHostConf->hosts[i].mig = sptrDefaultHost->mig;
            }

            if (INFINIT_INT == sptrHostConf->hosts[i].user_slot_limit
                && INFINIT_INT != sptrDefaultHost->user_slot_limit) {
                sptrHostConf->hosts[i].user_slot_limit = sptrDefaultHost->user_slot_limit;
            }

            if (NULL == sptrHostConf->hosts[i].dispatch_window
                && NULL != sptrDefaultHost->dispatch_window) {
                sptrHostConf->hosts[i].dispatch_window = copy_string(sptrDefaultHost->dispatch_window);
            }

            if (INFINIT_INT == sptrHostConf->hosts[i].max_jobs && 0 != sptrDefaultHost->max_jobs) {
                sptrHostConf->hosts[i].max_jobs = sptrDefaultHost->max_jobs;
            }

            if (NULL == sptrHostConf->hosts[i].load_schedule
                && NULL != sptrDefaultHost->load_schedule) {

                sptrHostConf->hosts[i].load_schedule = (float *)calloc(info->num_index, sizeof(float));
                if (NULL == sptrHostConf->hosts[i].load_schedule) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                "calloc", info->num_index);
                    quickpool_errno = ERROR_MEMORY;
                    return NULL;
                }

                unsigned int j;
                for (j = 0; j < info->num_index; j++) {
                    sptrHostConf->hosts[i].load_schedule[j] = sptrDefaultHost->load_schedule[j];
                }
            }

            if (NULL == sptrHostConf->hosts[i].load_stop && NULL != sptrDefaultHost->load_stop) {

                sptrHostConf->hosts[i].load_stop = (float *)calloc(info->num_index, sizeof(float));
                if (NULL == sptrHostConf->hosts[i].load_stop) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", info->num_index);
                    quickpool_errno = ERROR_MEMORY;
                    return NULL;
                }

                unsigned int j;
                for (j = 0; j < info->num_index; j++) {
                    sptrHostConf->hosts[i].load_stop[j] = sptrDefaultHost->load_stop[j];
                }
            }
        }
        free_hostinfo(sptrDefaultHost);
    }

    return (sptrHostConf);
} // end function readconf_host

/**
 * @brief       Read the qp.users file and fill the userConf structure. 
 *
 * @retval      Not NULL      #1: Pointer of the created userConf
 * @retval      NULL          #2: Failed to parse the qp.users file.
 *
 * @note        the file could be json or yaml format.
 *              2 sections in this file: user, usergroup.
 */
struct userConf *
readconf_user(void)
{
    quickpool_errno = 0;

    if (create_userconf()) {
        quickpool_errno = ERROR_CONF_FATAL;
        return NULL;
    }

    char file[PATH_MAX+1];
    FILE *fp;

    snprintf(file, PATH_MAX, "%s/qp.users", baseParams[BASE_CONFDIR].string_value);
    if ((fp = fopen(file, "r")) == NULL) {

        log_message(ERR, NOCLASS, "%s: failed to read file[%s]. Default users will be used.",
                    __func__, file);
        quickpool_errno = ERROR_CONF_FATAL;

        return NULL;
    }

    yaml_parser_t parser;
    if(!yaml_parser_initialize(&parser)) {

        log_message(ERR, NOCLASS, "%s: failed to initialize yaml parser!", __func__);
        quickpool_errno = ERROR_CONF_FATAL;
        fclose(fp);

        return NULL;
    }

    yaml_parser_set_input_file(&parser, fp);

    shashDefinedUsers = gf_hash_make(16);

    yaml_token_t token;
    char *strKey=NULL;
    do {

        yaml_parser_scan(&parser, &token);

        if (YAML_NO_ERROR != parser.error) {

            log_message(ERR, NOCLASS, "%s: error occurred when reading user file(%s) at line %d: %s",
                        __func__, file, parser.mark.line, parser.problem);

            quickpool_errno = ERROR_CONF_FATAL;
            yaml_parser_delete(&parser);
            fclose(fp);

            return NULL;
        }

        switch(token.type) {
        case YAML_SCALAR_TOKEN:

            strKey = (char *)token.data.scalar.value;
            if (NULL == strKey || 0 == strlen(strKey)) {
                continue;
            }

            if (strcasecmp(strKey, "user") == 0) {

                parse_user(&parser, file);
            } else if (strcasecmp(strKey, "usergroup") == 0) {

                parse_user_group(&parser, file);
            } else {
                log_message(ERR, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                            __func__, file, parser.mark.line, strKey);
                quickpool_errno = ERROR_CONF_WARNING;
            }
 
           break;
        default:
            break;
        }

        if(token.type != YAML_STREAM_END_TOKEN) {
            yaml_token_delete(&token);
        }
    } while(token.type != YAML_STREAM_END_TOKEN);
    yaml_token_delete(&token);

    yaml_parser_delete(&parser);
    fclose(fp);

    gf_hash_free(shashDefinedUsers, NULL);

    return (sptrUserConf);
} // end function readconf_user

/**
 * @brief       Read the qp.queues file and fill the queueConf structure. 
 *
 * @param[in]   info          #1: QuickPool information
 *
 * @retval      Not NULL      #1: Pointer of the created queueConf
 * @retval      NULL          #2: Failed to parse the qp.queues file.
 *
 * @note        the file could be json or yaml format.
 *              1 section in this file: queue.
 */
struct queueConf *
readconf_queue(struct quickpoolInfo *info)
{
    quickpool_errno = 0;
    if (info == NULL) {
        log_message(ERR, NOCLASS, NULL_POINTER_STR, __func__,  "qp_info");
        quickpool_errno = ERROR_CONF_FATAL;
        return NULL;
    }

    char file[PATH_MAX+1];
    FILE *fp;

    snprintf(file, PATH_MAX, "%s/qp.queues", baseParams[BASE_CONFDIR].string_value);
    if ((fp = fopen(file, "r")) == NULL) {

        log_message(ERR, NOCLASS, "%s: failed to read file[%s].", __func__, file);
        quickpool_errno = ERROR_CONF_FATAL;

        return NULL;
    }

    sptrQueueConf = (struct queueConf *)calloc(1, sizeof(struct queueConf));
    if (NULL == sptrQueueConf) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct queueConf));
        quickpool_errno = ERROR_CONF_FATAL;
        fclose(fp);

        return NULL;
    }

    yaml_parser_t parser;
    if(!yaml_parser_initialize(&parser)) {

        log_message(ERR, NOCLASS, "%s: failed to initialize yaml parser!", __func__);
        quickpool_errno = ERROR_CONF_FATAL;
        fclose(fp);

        return NULL;
    }

    yaml_parser_set_input_file(&parser, fp);

    bool_t queueok = FALSE;
    yaml_token_t token;
    char *strKey=NULL;
    do {

        yaml_parser_scan(&parser, &token);

        if (YAML_NO_ERROR != parser.error) {

            log_message(ERR, NOCLASS, "%s: error occurred when reading queue file(%s) at line %d: %s",
                        __func__, file, parser.mark.line, parser.problem);

            quickpool_errno = ERROR_CONF_FATAL;
            yaml_parser_delete(&parser);
            fclose(fp);

            return NULL;
        }

        switch(token.type) {
        case YAML_SCALAR_TOKEN:

            strKey = (char *)token.data.scalar.value;
            if (NULL == strKey || 0 == strlen(strKey)) {
                continue;
            }

            if (strcasecmp(strKey, "queue") == 0) {
                if (parse_queue(&parser, file, info)) {
                    queueok = TRUE;
                }
            } else {
                log_message(ERR, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                            __func__, file, parser.mark.line, strKey);
                quickpool_errno = ERROR_CONF_WARNING;
            }
 
           break;
        default:
            break;
        }

        if(token.type != YAML_STREAM_END_TOKEN) {
            yaml_token_delete(&token);
        }
    } while(token.type != YAML_STREAM_END_TOKEN);
    yaml_token_delete(&token);

    yaml_parser_delete(&parser);
    fclose(fp);

    if (quickpool_errno == ERROR_MEMORY) {

        quickpool_errno = ERROR_CONF_FATAL;

        return NULL;
    }

    if (!queueok) {
        log_message(ERR, NOCLASS, "%s: queue section missing.", __func__);
        quickpool_errno = ERROR_CONF_WARNING;
    }

    return sptrQueueConf;
} // end function readconf_queue

/**
 * @brief       Add host type to list. 
 *
 * @param[in]   type          #1: Type of a host to be added.
 *
 * @retval        -1          #1: Input type is NULL or reach the maximun number of host type.
 * @retval       >=0          #2: Index of the host type.
 *
 * @note        If host type is not in the list, add a new one.
 *              If host type is in the list, return its index.
 */
int
add_hosttype(char *type)
{
    if (NULL == type) {
        return -1;
    }

    if (MAXTYPES <= qpinfo.num_types) {
        log_message(ERR, NOCLASS, "%s: You can only define up to %d host types; host type %s ignored",
                    __func__, MAXTYPES, type);
        return -1;
    }

    unsigned int i;
    for (i=0;i<qpinfo.num_types;i++) {
        if (strcmp(qpinfo.host_types[i], type) != 0) {
            continue;
        }

        log_message(ERR, NOCLASS, "%s: host type %s multiply defined", __func__, type);
        return i;
    }

    strncpy(qpinfo.host_types[qpinfo.num_types], type, MAXNAMELEN);
    qpinfo.num_types++;

    return qpinfo.num_types-1;
} // end function add_hosttype

/* ------------------------------------------------
 *
 *  below are static functions used in this file
 *
 * ------------------------------------------------ */

/**
 * @brief       Get the resource index from resource list. 
 *
 * @param[in]   name          #1: The resource name
 *
 * @retval      -1            #1: Not found the resource in the list.
 * @retval       i            #2: Index of the resource in the list.
 *
 */
static int
get_resource_index(char *name)
{
    unsigned int i;

    if (name == NULL) {
        return -1;
    }

    for(i=0; i < qpinfo.num_resources; i++) {
        if (strcmp(name, qpinfo.resource_table[i].name) == 0) {
            return (i);
        }
    }

    return -1;
} // end function get_resource_index

/**
 * @brief       Parse HostType section in qp.shared file.
 *
 * @param[in]   parser        #1: yaml parser hanlder
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 *
 * @note        parse, check and add
 */
static bool_t
parse_hosttype(yaml_parser_t *parser, char *fname)
{
    yaml_token_t token;

    yaml_parser_scan(parser, &token);
    if (token.type != YAML_VALUE_TOKEN) {
        yaml_token_delete(&token);

        log_message(ERR, NOCLASS, "%s: there is format error in HostType section of file(%s) at line %d.",
                    __func__, fname, parser->mark.line);
        quickpool_errno = ERROR_CONF_WARNING;
        return FALSE;
    }
    yaml_token_delete(&token);

    struct config_param typeParam[] = {
        {"TYPENAME", STRING_PARAM, -1, {NULL}},
        {NULL, STRING_PARAM, -1, {NULL}}
    };

    char *strKey=NULL;
    unsigned int numMapping=0;
    int index=-1;
    do {
        yaml_parser_scan(parser, &token);

        if (YAML_NO_ERROR != parser->error) {

            log_message(ERR, NOCLASS, "%s: error occurred when parsing HostType section of file(%s) at line %d: %s",
                        __func__, fname, parser->mark.line, parser->problem);

            quickpool_errno = ERROR_CONF_WARNING;
            free_param_value(typeParam);

            return FALSE;
        }

        switch(token.type) {
        case YAML_FLOW_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_MAPPING_END_TOKEN:
            numMapping --;
            break;
        case YAML_FLOW_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_SEQUENCE_END_TOKEN:
            numMapping --;
            break;
        case YAML_BLOCK_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_END_TOKEN:
            numMapping --;
            break;
        case YAML_SCALAR_TOKEN:
            if (NULL == strKey) {
                index = match_param_key((char *)token.data.scalar.value, typeParam);
                if (index < 0) {

                    add_hosttype((char *)token.data.scalar.value);
                    break;
                }

                strKey = copy_string((char *)token.data.scalar.value);
            } else {
                if (strcasecmp(strKey, typeParam[0].param_name) == 0) {

                    add_hosttype((char *)token.data.scalar.value);
                }

                index = -1;
                FREEUP(strKey);
            }

            break;
        default:
            break;
        }

        if ((token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN) || numMapping > 0) {
            yaml_token_delete(&token);
        }
    } while (token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN);
    yaml_token_delete(&token);

    free_param_value(typeParam);

    return TRUE;
} // end function parse_hosttype

/**
 * @brief       Add host model to list. 
 *
 * @param[in]   param         #1: host model parameter.
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, reach limit or duplicate
 */
static char
add_hostmodel(struct config_param *param)
{
    unsigned int i;

#define MODELNAME_INDEX    0
#define CPUFACTOR_INDEX    1
#define ARCHITECTURE_INDEX 2
    if (NULL == param[MODELNAME_INDEX].string_value) {
        return FALSE;
    }

    if (qpinfo.num_models == MAXMODELS) {
        log_message(ERR, NOCLASS, "%s: Too many host models defined in section HostModel. You can only define up to %d host models",
                    __func__, MAXMODELS, param[MODELNAME_INDEX].string_value);
        return FALSE;
    }

    for (i = 0; i < qpinfo.num_models; ++i) {
        if (param[ARCHITECTURE_INDEX].param_name == NULL
            || strcmp(qpinfo.host_archs[i], param[ARCHITECTURE_INDEX].param_name) != 0) {
            continue;
        }

        log_message(ERR, NOCLASS, "%s: Duplicate host architecture type found in section HostModel. Architecture type must be unique",
                    __func__, param[MODELNAME_INDEX].string_value);

        return FALSE;
    }

    strncpy(qpinfo.host_models[qpinfo.num_models], param[MODELNAME_INDEX].string_value, MAXNAMELEN);
    strncpy(qpinfo.host_archs[qpinfo.num_models],
            param[ARCHITECTURE_INDEX].string_value?param[ARCHITECTURE_INDEX].string_value: "",
            MAXNAMELEN);
    qpinfo.cpu_factor[qpinfo.num_models] = param[CPUFACTOR_INDEX].float_value;
    qpinfo.num_models++;

    return TRUE;
} // end function add_hostmodel

/**
 * @brief       Parse HostModel section in qp.shared file.
 *
 * @param[in]   parser        #1: yaml parser hanlder
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 *
 * @note        parse, check and add
 */
static bool_t
parse_hostmodel(yaml_parser_t *parser, char *fname)
{
    yaml_token_t token;

    yaml_parser_scan(parser, &token);
    if (token.type != YAML_VALUE_TOKEN) {
        yaml_token_delete(&token);

        log_message(ERR, NOCLASS, "%s: there is format error in HostModel section of file(%s) at line %d.",
                    __func__, fname, parser->mark.line);
        quickpool_errno = ERROR_CONF_WARNING;
        return FALSE;
    }
    yaml_token_delete(&token);

    struct config_param modelParam[] = {
        {"MODELNAME", STRING_PARAM, -1, {NULL}},
        {"CPUFACTOR", FLOAT_PARAM, -1, {0}},
        {"ARCHITECTURE", STRING_PARAM, -1, {NULL}},
        {NULL, STRING_PARAM, -1, {NULL}}
    };

    char *strKey=NULL;
    unsigned int numMapping=0, numModel=0;
    int index=-1;
    do {
        yaml_parser_scan(parser, &token);

        if (YAML_NO_ERROR != parser->error) {

            log_message(ERR, NOCLASS, "%s: error occurred when parsing HostModel section of file(%s) at line %d: %s",
                        __func__, fname, parser->mark.line, parser->problem);

            quickpool_errno = ERROR_CONF_WARNING;
            free_param_value(modelParam);

            return FALSE;
        }

        switch(token.type) {
        case YAML_FLOW_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_MAPPING_END_TOKEN:
            numMapping --;
            break;
        case YAML_FLOW_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_SEQUENCE_END_TOKEN:
            numMapping --;
            break;
        case YAML_BLOCK_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_END_TOKEN:
            numMapping --;
            break;
        case YAML_SCALAR_TOKEN:
            if (NULL == strKey) {
                index = match_param_key((char *)token.data.scalar.value, modelParam);
                if (index < 0) {
                    log_message(WARNING, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                                __func__, fname, parser->mark.line, (char *)token.data.scalar.value);
                    quickpool_errno = ERROR_CONF_WARNING;
                    break;
                }

                strKey = copy_string((char *)token.data.scalar.value);
            } else {
                if (0 == strcasecmp(strKey, modelParam[MODELNAME_INDEX].param_name)) {
                    numModel ++;
                    if (numModel > 1) {
                        add_hostmodel(modelParam);
                        reset_param_value(modelParam);
                    }
                }

                if (!set_param_value(index, (char *)token.data.scalar.value, modelParam)) {
                    log_message(ERR, NOCLASS, "%s: error occurred when parsing HostModel section of file(%s) at line %d: see above for reason",
                                __func__, fname, parser->mark.line);
                    quickpool_errno = ERROR_CONF_WARNING;
                }

                index = -1;
                FREEUP(strKey);
            }

            break;
        default:
            break;
        }

        if ((token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN) || numMapping > 0) {
            yaml_token_delete(&token);
        }
    } while (token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN);
    yaml_token_delete(&token);

    if (numModel > 0) {
        add_hostmodel(modelParam);
    }

    free_param_value(modelParam);

    return TRUE;
} // end function parse_hostmodel

/**
 * @brief       Get type of value: Boolean, String or Numeric.
 *
 * @param[in]   type          #1: string of type
 *
 * @retval      TYPE_NUMERIC  #1: numeric value
 * @retval      TYPE_BOOLEAN  #2: boolean value
 * @retval      TYPE_STRING   #3: string value
 *
 * @note        Translate resource type
 */
static value_type_t
get_value_type(char *type)
{
    if (NULL == type) {
        return TYPE_NUMERIC;
    }

    if (!strcasecmp(type, "Boolean")) {
        return (TYPE_BOOLEAN);
    }

    if (!strcasecmp(type, "String")) {
        return (TYPE_STRING);
    }

    if (!strcasecmp(type, "Numeric")) {
        return (TYPE_NUMERIC);
    }

    return TYPE_NUMERIC;
} // end function get_value_type

/**
 * @brief       Allocate a new space for resource table.
 *
 * @retval      0             #1: succeed
 * @retval      -1            #2: failed, not enough memory
 *
 * @note        Resource table space is full, realloc a new space.
 */
static int
enlarge_resource_table(void)
{
    if (0 >= sintMaxResource) {
        return -1;
    }

    unsigned int size = 2*sintMaxResource*sizeof(struct resource_definition);
    struct resource_definition *tempTable;
    tempTable = (struct resource_definition *)realloc(qpinfo.resource_table, size);
    if (NULL == tempTable) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL, __func__, "realloc", size);
        return -1;
    }

    qpinfo.resource_table = tempTable;
    sintMaxResource += 1000;

    return 0;
} // end function enlarge_resource_table

/**
 * @brief       Check syntax of resource definition.
 *
 * @param[in]   param         #1: resource definition parameters
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 */
static bool_t
check_resource_definition(struct config_param *param, char *fname)
{
#define RESOURCENAME_INDEX 0
#define TYPE_INDEX         1
#define INTERVAL_INDEX     2
#define INCREASING_INDEX   3
#define RELEASE_INDEX      4
#define CONSUMABLE_INDEX   5
#define DESCRIPTION_INDEX  6

    if (NULL == param[RESOURCENAME_INDEX].string_value) {
        log_message(ERR, NOCLASS, "%s: In %s resource section, resource name must be specified for a resource definition.",
                    __func__, fname);

        return FALSE;
    }

    if (strpbrk(param[RESOURCENAME_INDEX].string_value, ILLEGAL_CHARS) != NULL) {
        log_message(ERR, NOCLASS, "%s: In %s resource section, illegal character (one of %s) in resource name <%s>.",
                    __func__, fname, ILLEGAL_CHARS, param[RESOURCENAME_INDEX].string_value);

        return FALSE;
    }

    if (isdigit(param[RESOURCENAME_INDEX].string_value[0])) {
        log_message(ERR, NOCLASS, "%s: In %s resource section, resource name <%s> begin with a digit is illegal",
                    __func__, fname, param[RESOURCENAME_INDEX].string_value);

        return FALSE;
    }

    if (qpinfo.num_resources >= sintMaxResource && enlarge_resource_table() < 0) {

        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "enlarge_resource_table");

        return FALSE;
    }

    int resIndex;
    if ((resIndex = get_resource_index(param[RESOURCENAME_INDEX].string_value)) >= 0) {
        if ((qpinfo.resource_table[resIndex].flags & RESOURCE_FLAG_BUILTIN)
            && (qpinfo.resource_table[resIndex].flags & RESOURCE_FLAG_DYNAMIC)) {

            value_type_t type = get_value_type(param[TYPE_INDEX].string_value);
            bool_t order = param[INCREASING_INDEX].bool_value;

            if (NULL != param[TYPE_INDEX].string_value
                && qpinfo.resource_table[resIndex].value_type == type
                && qpinfo.resource_table[resIndex].order_type == order?INCR:DECR){

                qpinfo.resource_table[resIndex].flags &= ~RESOURCE_FLAG_BUILTIN;
            } else {

                log_message(ERR, NOCLASS, "%s: In %s resource section, built-in resource %s can't be overrided with different type or increasing.",
                            __func__, fname, param[RESOURCENAME_INDEX].string_value);

                return FALSE;
            }
        } else {
            log_message(ERR, NOCLASS, "%s: In %s resource section, resource %s is reserved or previously defined.",
                        __func__, fname, param[RESOURCENAME_INDEX].string_value);

            return FALSE;
        }
    }

    return TRUE;
} // end function check_resource_definition

/**
 * @brief       Add resource definition to cluster resource table.
 *
 * @param[in]   param         #1: resource definition parameters
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 */
static bool_t
add_resource(struct config_param *param, char *fname)
{
    struct resource_definition *res = &(qpinfo.resource_table[qpinfo.num_resources]);

    res->flags = RESOURCE_FLAG_GLOBAL;
    res->value_type = TYPE_BOOLEAN;
    res->order_type = NA;
    res->interval = 0;

    res->name = copy_string(param[RESOURCENAME_INDEX].string_value);

    if (NULL != param[TYPE_INDEX].string_value && '\0' != param[TYPE_INDEX].string_value[0]) {
        res->value_type = get_value_type(param[TYPE_INDEX].string_value);
    }

    res->interval = param[INTERVAL_INDEX].int_value;

    if (TYPE_NUMERIC == res->value_type) {

        if (param[INCREASING_INDEX].bool_value) {
            res->order_type = INCR;
        } else {
            res->order_type = DECR;
        }

        if (param[RELEASE_INDEX].bool_value) {
            res->flags |= RESOURCE_FLAG_RELEASE;
        }

        if (param[CONSUMABLE_INDEX].bool_value) {
            res->flags |= RESOURCE_FLAG_CONSUMABLE;
        }
    }

    res->des = copy_string(param[DESCRIPTION_INDEX].string_value);

    if (0 < res->interval && TYPE_NUMERIC == res->value_type) {

        res->flags |= RESOURCE_FLAG_DYNAMIC;
        qpinfo.num_user_index++;
        qpinfo.num_index++;
    }

    qpinfo.num_resources++;

    return TRUE;
} // end function add_resource

/**
 * @brief       Parse Resource section in qp.shared file.
 *
 * @param[in]   parser        #1: yaml parser hanlder
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 *
 * @note        parse, check and add
 */
static bool_t
parse_resource(yaml_parser_t *parser, char *fname)
{
    yaml_token_t token;

    yaml_parser_scan(parser, &token);
    if (token.type != YAML_VALUE_TOKEN) {
        yaml_token_delete(&token);

        log_message(ERR, NOCLASS, "%s: there is format error in resource section of file(%s) at line %d.",
                    __func__, fname, parser->mark.line);

        quickpool_errno = ERROR_CONF_WARNING;
        return FALSE;
    }
    yaml_token_delete(&token);

    /* see check_resource_definition() for index macro definition. */
    struct config_param resourceParam[] = {
        {"RESOURCENAME", STRING_PARAM, -1, {NULL}},
        {"TYPE", STRING_PARAM, -1, {NULL}},
        {"INTERVAL", INT_PARAM, -1, {0}},
        {"INCREASING", BOOL_PARAM, -1, {FALSE}},
        {"RELEASE", BOOL_PARAM, -1, {FALSE}},
        {"CONSUMABLE", BOOL_PARAM, -1, {FALSE}},
        {"DESCRIPTION", STRING_PARAM, -1, {NULL}},
        {NULL, STRING_PARAM, -1, {NULL}}
    };
    resourceParam[INTERVAL_INDEX].int_value = -1;

    char *strKey=NULL;
    unsigned int numMapping=0, numResource=0;
    int index=-1;
    do {
        yaml_parser_scan(parser, &token);

        if (YAML_NO_ERROR != parser->error) {

            log_message(ERR, NOCLASS, "%s: error occurred when parsing resource section of file(%s) at line %d: %s",
                        __func__, fname, parser->mark.line, parser->problem);

            quickpool_errno = ERROR_CONF_WARNING;
            free_param_value(resourceParam);

            return FALSE;
        }

        switch(token.type) {
        case YAML_FLOW_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_MAPPING_END_TOKEN:
            numMapping --;
            break;
        case YAML_FLOW_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_SEQUENCE_END_TOKEN:
            numMapping --;
            break;
        case YAML_BLOCK_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_END_TOKEN:
            numMapping --;
            break;
        case YAML_SCALAR_TOKEN:
            if (NULL == strKey) {
                index = match_param_key((char *)token.data.scalar.value, resourceParam);
                if (index < 0) {
                    log_message(WARNING, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                                __func__, fname, parser->mark.line, (char *)token.data.scalar.value);
                    quickpool_errno = ERROR_CONF_WARNING;
                    break;
                }

                strKey = copy_string((char *)token.data.scalar.value);
            } else {

                if (0 == strcasecmp(strKey, resourceParam[RESOURCENAME_INDEX].param_name)) {
                    numResource ++;

                    if (numResource > 1) {

                        if (check_resource_definition(resourceParam, fname)) {
                            add_resource(resourceParam, fname);
                        }

                        reset_param_value(resourceParam);
                        resourceParam[INTERVAL_INDEX].int_value = -1;
                    }
                }

                if (!set_param_value(index, (char *)token.data.scalar.value, resourceParam)) {
                    log_message(ERR, NOCLASS, "%s: error occurred when parsing resource section of file(%s) at line %d: see above for reason",
                                __func__, fname, parser->mark.line);
                    quickpool_errno = ERROR_CONF_WARNING;
                }

                index = -1;
                FREEUP(strKey);
            }

            break;
        default:
            break;
        }

        if ((token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN) || numMapping > 0) {
            yaml_token_delete(&token);
        }
    } while (token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN);
    yaml_token_delete(&token);

    if (numResource > 0 && check_resource_definition(resourceParam, fname)) {

        add_resource(resourceParam, fname);
    }

    free_param_value(resourceParam);

    return TRUE;
} // end function parse_resource

/**
 * @brief       Parse Cluster section in qp.cluster.xxx file.
 *
 * @param[in]   parser        #1: yaml parser hanlder
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 *
 * @note        parse, check and add
 */
static bool_t
parse_cluster(yaml_parser_t *parser, char *fname)
{
    yaml_token_t token;

    yaml_parser_scan(parser, &token);
    if (token.type != YAML_VALUE_TOKEN) {
        yaml_token_delete(&token);

        log_message(ERR, NOCLASS, "%s: there is format error in cluster section of file(%s) at line %d.",
                    __func__, fname, parser->mark.line);
        quickpool_errno = ERROR_CONF_WARNING;
        return FALSE;
    }
    yaml_token_delete(&token);

    struct config_param clusterParam[] = {
        {"CLUSTERNAME", STRING_PARAM, -1, {NULL}},
        {"SERVERS", STRING_PARAM, -1, {NULL}},
        {NULL, STRING_PARAM, -1, {NULL}}
    };

    char *strKey=NULL;
    unsigned int numMapping=0, numCluster=0;
    int index=-1;
    do {
        yaml_parser_scan(parser, &token);

        if (YAML_NO_ERROR != parser->error) {

            log_message(ERR, NOCLASS, "%s: error occurred when parsing cluster section of file(%s) at line %d: %s",
                        __func__, fname, parser->mark.line, parser->problem);

            quickpool_errno = ERROR_CONF_WARNING;
            return FALSE;
        }

        switch(token.type) {
        case YAML_FLOW_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_MAPPING_END_TOKEN:
            numMapping --;
            break;
        case YAML_FLOW_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_SEQUENCE_END_TOKEN:
            numMapping --;
            break;
        case YAML_BLOCK_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_END_TOKEN:
            numMapping --;
            break;
        case YAML_SCALAR_TOKEN:
            if (NULL == strKey) {
                index = match_param_key((char *)token.data.scalar.value, clusterParam);
                if (index < 0) {
                    log_message(WARNING, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                                __func__, fname, parser->mark.line, (char *)token.data.scalar.value);
                    quickpool_errno = ERROR_CONF_WARNING;
                    break;
                }

                strKey = copy_string((char *)token.data.scalar.value);
            } else {
                if (strcasecmp(strKey, clusterParam[0].param_name) == 0) {
                    if (0 < numCluster) {
                        log_message(ERR, NOCLASS, "%s: In %s, cannot specify multiple clusters.",
                                    __func__, fname);
                        quickpool_errno = ERROR_CONF_WARNING;
                        break;
                    }

                    gptrSharedConf->cluster_name = copy_string((char *)token.data.scalar.value);
                    numCluster ++;
                } else if (strcasecmp(strKey, clusterParam[1].param_name) == 0) {
                    FREEUP(gptrSharedConf->servers);
                    gptrSharedConf->servers = copy_string((char *)token.data.scalar.value);
                }

                index = -1;
                FREEUP(strKey);
            }

            break;
        default:
            break;
        }

        if ((token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN) || numMapping > 0) {
            yaml_token_delete(&token);
        }
    } while (token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN);
    yaml_token_delete(&token);

    return TRUE;
} // end function parse_cluster

/**
 * @brief       Add cluster adminstrator to cluster conf.
 *
 * @param[in]   param         #1: admin parameters
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 */
static bool_t
add_admin(struct config_param *param, char *fname)
{
    char *sp = param[0].string_value;

    unsigned int numAds = 0;
    unsigned int len = strlen(sp);
    char *strAdmin = (char *)calloc(len+1, sizeof(char));
    if (NULL == strAdmin) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, len+1);
        quickpool_errno = ERROR_MEMORY;
        return FALSE;
    }
    char *token;
    while ((token = get_string_token(&sp, " ,", strAdmin, len+1)) != NULL) {
        len = strlen(sp);
        numAds++;
    }

    struct admins *admin = gptrClusterConf->admin;
    if (numAds > 0) {
        admin->admin_ids = (uid_t *)calloc(numAds, sizeof(uid_t));
        admin->admin_gids = (uid_t *)calloc(numAds, sizeof(uid_t));
        admin->admin_names = (char **)calloc(numAds, sizeof(char *));
        if (NULL == admin->admin_ids || NULL == admin->admin_gids || NULL == admin->admin_names) {

            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", numAds*(sizeof(uid_t)*2+sizeof(char*)));
            quickpool_errno = ERROR_MEMORY;
            return FALSE;
        }
    }

    struct  group *unixGrp;
    sp = param[0].string_value;
    len = strlen(sp);
    while ((token = get_string_token(&sp, " ,", strAdmin, len+1)) != NULL) {
        if (NULL != get_user_byname(token)) {
            addmore_admins(token, admin, &numAds);
        } else if ((unixGrp = get_group_byname(token)) != NULL) {
            int i=0;

            while (unixGrp->gr_mem[i] != NULL) {
                addmore_admins(unixGrp->gr_mem[i++], admin, &numAds);
            }

            continue;
        } else {
            addmore_admins(token, admin, &numAds);
        }
    }
    FREEUP(strAdmin);

    return TRUE;
} // end function add_admin

/**
 * @brief       Parse ClusterAdmins section in qp.cluster.xxx file.
 *
 * @param[in]   parser        #1: yaml parser hanlder
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 *
 * @note        parse, check and add
 */
static bool_t
parse_cluster_admin(yaml_parser_t *parser, char *fname)
{
    yaml_token_t token;

    yaml_parser_scan(parser, &token);
    if (token.type != YAML_VALUE_TOKEN) {
        yaml_token_delete(&token);

        log_message(ERR, NOCLASS, "%s: there is format error in ClusterAdmins section of file(%s) at line %d.",
                    __func__, fname, parser->mark.line);
        quickpool_errno = ERROR_CONF_WARNING;
        return FALSE;
    }
    yaml_token_delete(&token);

    struct config_param adminParam[] = {
        {"ADMINISTRATORS", STRING_PARAM, -1, {NULL}},
        {NULL, STRING_PARAM, -1, {NULL}}
    };

    char *strKey=NULL;
    unsigned int numMapping=0;
    int index=-1;
    do {
        yaml_parser_scan(parser, &token);

        if (YAML_NO_ERROR != parser->error) {

            log_message(ERR, NOCLASS, "%s: error occurred when parsing ClusterAdmins section of file(%s) at line %d: %s",
                        __func__, fname, parser->mark.line, parser->problem);

            free_param_value(adminParam);
            quickpool_errno = ERROR_CONF_WARNING;
            return FALSE;
        }

        switch(token.type) {
        case YAML_FLOW_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_MAPPING_END_TOKEN:
            numMapping --;
            break;
        case YAML_FLOW_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_SEQUENCE_END_TOKEN:
            numMapping --;
            break;
        case YAML_BLOCK_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_END_TOKEN:
            numMapping --;
            break;
        case YAML_SCALAR_TOKEN:
            if (NULL == strKey) {
                index = match_param_key((char *)token.data.scalar.value, adminParam);
                if (index < 0) {
                    log_message(WARNING, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                                __func__, fname, parser->mark.line, (char *)token.data.scalar.value);
                    quickpool_errno = ERROR_CONF_WARNING;
                    break;
                }

                strKey = copy_string((char *)token.data.scalar.value);
            } else {
                if (strcasecmp(strKey, adminParam[0].param_name) == 0) {

                    if (!set_param_value(index, (char *)token.data.scalar.value, adminParam)) {
                        log_message(ERR, NOCLASS, "%s: error occurred when parsing ClusterAdmins section of file(%s) at line %d: see above for reason",
                                    __func__, fname, parser->mark.line);
                        quickpool_errno = ERROR_CONF_WARNING;
                    }

                    add_admin(adminParam, fname);
                }

                index = -1;
                FREEUP(strKey);
            }

            break;
        default:
            break;
        }

        if ((token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN) || numMapping > 0) {
            yaml_token_delete(&token);
        }
    } while (token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN);
    yaml_token_delete(&token);

    free_param_value(adminParam);

    return TRUE;
} // end function parse_cluster_admin

/**
 * @brief       Parse Parameters section in qp.cluster.xxx file.
 *
 * @param[in]   parser        #1: yaml parser hanlder
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 *
 * @note        parse, check and add
 */
static bool_t
parse_cluster_param(yaml_parser_t *parser, char *fname)
{
    yaml_token_t token;

    yaml_parser_scan(parser, &token);
    if (token.type != YAML_VALUE_TOKEN) {
        yaml_token_delete(&token);

        log_message(ERR, NOCLASS, "%s: there is format error in Parameters section of file(%s).",
                    __func__, fname);
        quickpool_errno = ERROR_CONF_WARNING;
        return FALSE;
    }
    yaml_token_delete(&token);

    char *strKey=NULL;
    unsigned int numMapping=0;
    int index=-1;
    do {
        yaml_parser_scan(parser, &token);

        if (YAML_NO_ERROR != parser->error) {

            log_message(ERR, NOCLASS, "%s: error occurred when parsing Parameters section of file(%s) at line %d : %s",
                        __func__, fname, parser->mark.line, parser->problem);

            quickpool_errno = ERROR_CONF_WARNING;
            return FALSE;
        }

        switch(token.type) {
        case YAML_FLOW_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_MAPPING_END_TOKEN:
            numMapping --;
            break;
        case YAML_FLOW_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_SEQUENCE_END_TOKEN:
            numMapping --;
            break;
        case YAML_BLOCK_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_END_TOKEN:
            numMapping --;
            break;
        case YAML_SCALAR_TOKEN:
            if (NULL == strKey) {
                index = match_param_key((char *)token.data.scalar.value, gptrClusterConf->param);
                if (index < 0) {
                    log_message(WARNING, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                                __func__, fname, parser->mark.line, (char *)token.data.scalar.value);
                    quickpool_errno = ERROR_CONF_WARNING;
                    break;
                }

                strKey = copy_string((char *)token.data.scalar.value);
            } else {
                if (!set_param_value(index, (char *)token.data.scalar.value, gptrClusterConf->param)) {
                    log_message(ERR, NOCLASS, "%s: error occurred when parsing Parameters section of file(%s) at line %d: see above for reason",
                                __func__, fname, parser->mark.line);
                    quickpool_errno = ERROR_CONF_WARNING;
                }

                index = -1;
                FREEUP(strKey);
            }

            break;
        default:
            break;
        }

        if ((token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN) || numMapping > 0) {
            yaml_token_delete(&token);
        }
    } while (token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN);
    yaml_token_delete(&token);

    return TRUE;
} // end function parse_cluster_param

/**
 * @brief       Check syntax of host parameters.
 *
 * @param[in]   param         #1: host info parameters
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 */
static bool_t
check_host_definition(struct config_param *param, char *fname)
{
#define HOSTNAME_INDEX         qpinfo.num_index+0
#define MODEL_INDEX            qpinfo.num_index+1
#define HOSTTYPE_INDEX         qpinfo.num_index+2
#define RESOURCES_INDEX        qpinfo.num_index+3
#define SERVER_INDEX           qpinfo.num_index+4
#define RUNWINDOW_INDEX        qpinfo.num_index+5

    if (NULL == param[HOSTNAME_INDEX].string_value) {

        log_message(ERR, NOCLASS, "%s: In %s host section, host name must be specified for a host definition.",
                    __func__, fname);

        return FALSE;
    }

    LIST_T *nodeNames = gf_list_make("cluster name list");

    node_base_name(param[HOSTNAME_INDEX].string_value,nodeNames);
    LIST_ITERATOR_T iter;
    gf_listiterator_link(&iter,nodeNames);

    LIST_PROXY_T *pxy;
    for(pxy = (LIST_PROXY_T *)gf_listiterator_curentry(&iter);
        !gf_listiterator_isend(&iter);
        gf_listiterator_getnext(&iter, (LIST_T **)&pxy)) {
            
            char *strNodeName = (char *)pxy->subject;
            
            if (NULL == gf_gethostbyname(strNodeName)) {
                log_message(ERR, NOCLASS, "%s: In %s host section: host name <%s> is not recognized.",
                            __func__, fname, param[HOSTNAME_INDEX].string_value);
                
                gf_list_free(nodeNames,gf_free_proxy_string);
                return FALSE;
            }

            unsigned int i;
            for (i=0; i<gptrClusterConf->num_hosts; i++) {

                if (!issame_host(gptrClusterConf->hosts[i].host, strNodeName)) {
                    continue;
                }

                break;
            }

            if (i < gptrClusterConf->num_hosts) {

                log_message(WARNING, NOCLASS, "%s: In %s host section, host name <%s> is multiply defined",
                            __func__, fname, param[HOSTNAME_INDEX].string_value);

                gf_list_free(nodeNames,gf_free_proxy_string);
                return FALSE;
            }
        }
    gf_list_free(nodeNames,gf_free_proxy_string);
    return TRUE;
} // end function check_host_definition

/**
 * @brief       Add host to host list of cluster conf.
 *
 * @param[in]   param         #1: host info parameters
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, not enough memory
 */
static bool_t
add_conf_host(struct config_param *param, char *fname)
{
    struct QuickPoolHostInfo *newList;

    LIST_T *nodeNames = gf_list_make("cluster name list");

    int num = 0;
    num = node_base_name(param[HOSTNAME_INDEX].string_value,nodeNames);
    if (gptrClusterConf->hosts == NULL) {
        newList = (struct QuickPoolHostInfo *)calloc(num, sizeof(struct QuickPoolHostInfo));
    } else {
        num+=gptrClusterConf->num_hosts;
        newList = (struct QuickPoolHostInfo *)realloc(gptrClusterConf->hosts, num*sizeof(struct QuickPoolHostInfo));
    }
    
    if (NULL == newList) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "realloc",
                    gptrClusterConf->num_hosts*sizeof(struct QuickPoolHostInfo));

        quickpool_errno = ERROR_MEMORY;
        gf_list_free(nodeNames, NULL);
        return FALSE;
    }

    gptrClusterConf->hosts = newList;
    
    LIST_ITERATOR_T iter;
    gf_listiterator_link(&iter,nodeNames);

    LIST_PROXY_T *pxy;
    for(pxy = (LIST_PROXY_T *)gf_listiterator_curentry(&iter);
        !gf_listiterator_isend(&iter);
        gf_listiterator_getnext(&iter, (LIST_T **)&pxy)) {
            
            char *nodeName = (char *)pxy->subject;

            struct QuickPoolHostInfo *hostAdd = &(gptrClusterConf->hosts[gptrClusterConf->num_hosts]);
            memset(hostAdd, 0, sizeof(struct QuickPoolHostInfo));
            
            hostAdd->host = nodeName;

            if (NULL != param[HOSTTYPE_INDEX].string_value && '!' != param[HOSTTYPE_INDEX].string_value[0]) {
                hostAdd->host_type = copy_string(param[HOSTTYPE_INDEX].string_value);
            }

            if (NULL != param[MODEL_INDEX].string_value && '!' != param[MODEL_INDEX].string_value[0]) {
                hostAdd->host_model = copy_string(param[MODEL_INDEX].string_value);
            }

            hostAdd->load_threshold = (double *)calloc(gptrSharedConf->qpinfo->num_index, sizeof(double));
            if (NULL == hostAdd->load_threshold) {

                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                            "calloc", gptrSharedConf->qpinfo->num_index*sizeof(double));

                quickpool_errno = ERROR_MEMORY;
                gf_list_free(nodeNames, NULL);
                return FALSE;
            }

            unsigned int i;
            for (i=0; i<qpinfo.num_index; i++) {
                hostAdd->load_threshold[i] = param[i].float_value;
            }

            if (hostAdd->load_threshold[UT] > 1.0 && hostAdd->load_threshold[UT] < INFINIT_LOAD) {

                log_message(WARNING, NOCLASS, "%s: In %s host section, value for threshold ut <%2.2f> is greater than 1, set it to <%2.2f%%>",
                            __func__, fname, hostAdd->load_threshold[UT], hostAdd->load_threshold[UT]/100);

                hostAdd->load_threshold[UT] /= 100.0;
            }

            if (NULL != param[RESOURCES_INDEX].string_value) {
                char  *sp;
                sp = param[RESOURCES_INDEX].string_value;
                unsigned int len = strlen(sp);
                char *resource = (char *)calloc(len+1, sizeof(char));
                char *token;
                while ((token = get_string_token(&sp, " ", resource, len+1)) != NULL) {
                    len = strlen(sp);

                    if (token[0] == '!') {
                        token ++;
                    }

                    char *value = strstr(token, "=");
                    if (NULL != value) {
                        *value = 0;
                        value ++;
                    }

                    int resIndex;
                    if ((resIndex = get_resource_index(token)) < 0)  {

                        log_message(ERR, NOCLASS, "%s: unknown resource name <%s> is specified for host %s in file %s host section.",
	                        __func__, token, hostAdd->host, fname);
                        continue;
                    }

                    if (NULL == value
                        && TYPE_BOOLEAN != gptrSharedConf->qpinfo->resource_table[resIndex].value_type) {

                        log_message(ERR, NOCLASS, "%s: must specify a value for numeric and string resource <%s> for host %s in file %s host section.",
                                    __func__, token, hostAdd->host, fname);
                        continue;
                    }

                    struct config_param locationParam[3];
                    locationParam[0].param_name = "RESOURCE_NAME";
                    locationParam[0].param_type = STRING_PARAM;
                    locationParam[0].string_value = copy_string(token);

                    char *loc=NULL;
                    unsigned int size=0;
                    if (NULL != value) {

                        size = strlen(value) + strlen(hostAdd->host) + 4;
                        loc = (char *)calloc(size, sizeof(char));

                        if (TYPE_BOOLEAN == gptrSharedConf->qpinfo->resource_table[resIndex].value_type) {
                            int boolVal = 1;
                            if (0 == strcasecmp(value, "No") || 0 == strcasecmp(value, "N")) {
                                boolVal = 0;
                            }
                            snprintf(loc, size, "%d@[%s]", boolVal, hostAdd->host);
                        } else {
                            snprintf(loc, size, "%s@[%s]", value, hostAdd->host);
                        }
                    } else {

                        size = 10 + strlen(hostAdd->host) + 4;
                        loc = (char *)calloc(size, sizeof(char));
                        snprintf(loc, size, "%d@[%s]", 1, hostAdd->host);
                    }

                    if (NULL == loc) {

                        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", size);
                        quickpool_errno = ERROR_MEMORY;
                        gf_list_free(nodeNames, NULL);

                        return FALSE;
                    }

                    locationParam[1].param_name = "LOCATION";
                    locationParam[1].param_type = STRING_PARAM;
                    locationParam[1].string_value = loc;

                    locationParam[2].param_name = NULL;
                    locationParam[2].param_type = STRING_PARAM;
                    locationParam[2].string_value = NULL;

                    add_resource_map(locationParam, fname);
                    free_param_value(locationParam);
                }

                FREEUP(resource);
            }

            if (0 < param[RUNWINDOW_INDEX].index && NULL != param[RUNWINDOW_INDEX].string_value) {

                hostAdd->run_window = parse_time_window(param[RUNWINDOW_INDEX].string_value, NULL, "host");
                if (NULL == hostAdd->run_window) {
                    quickpool_errno = ERROR_CONF_WARNING;
                }
            }

            hostAdd->server = STATIC_SERVER;
            if (0 > param[SERVER_INDEX].index) {
                hostAdd->server = STATIC_SERVER;
            } else {
                if (!param[SERVER_INDEX].bool_value) {
                    hostAdd->server = CLIENT;
                }
            }

            gptrClusterConf->num_hosts++;
        }
    
    gf_list_free(nodeNames, NULL);
    return TRUE;
} // end function add_conf_host

/**
 * @brief       Parse Host section in qp.cluster.xxx file.
 *
 * @param[in]   parser        #1: yaml parser hanlder
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 *
 * @note        parse, check and add
 */
static bool_t
parse_node(yaml_parser_t *parser, char *fname)
{
    yaml_token_t token;

    yaml_parser_scan(parser, &token);
    if (token.type != YAML_VALUE_TOKEN) {
        yaml_token_delete(&token);

        log_message(ERR, NOCLASS, "%s: there is format error in host section of file(%s) at line %d.",
                    __func__, fname, parser->mark.line);
        quickpool_errno = ERROR_CONF_WARNING;
        return FALSE;
    }
    yaml_token_delete(&token);

    struct config_param *hostParam;
    hostParam = (struct config_param *)calloc(qpinfo.num_index + 8, sizeof(struct config_param));
    if (NULL == hostParam) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", (qpinfo.num_index+8)*sizeof(struct config_param));
        quickpool_errno = ERROR_MEMORY;

        return FALSE;
    }

    unsigned int i;
    for (i=0; i<qpinfo.num_index; i++) {

        hostParam[i].param_name = qpinfo.resource_table[i].name;
        hostParam[i].param_type = FLOAT_PARAM;
        hostParam[i].index = -1;

        if (qpinfo.resource_table[i].order_type == INCR) {
            hostParam[i].float_value = INFINIT_LOAD;
        } else {
            hostParam[i].float_value = -INFINIT_LOAD;
        }
    }

    hostParam[HOSTNAME_INDEX].param_name = "HOSTNAME";
    hostParam[HOSTNAME_INDEX].param_type = STRING_PARAM;
    hostParam[HOSTNAME_INDEX].index = -1;
    hostParam[MODEL_INDEX].param_name = "MODEL";
    hostParam[MODEL_INDEX].param_type = STRING_PARAM;
    hostParam[MODEL_INDEX].index = -1;
    hostParam[HOSTTYPE_INDEX].param_name = "TYPE";
    hostParam[HOSTTYPE_INDEX].param_type = STRING_PARAM;
    hostParam[HOSTTYPE_INDEX].index = -1;
    hostParam[RESOURCES_INDEX].param_name = "RESOURCES";
    hostParam[RESOURCES_INDEX].param_type = STRING_PARAM;
    hostParam[RESOURCES_INDEX].index = -1;
    hostParam[SERVER_INDEX].param_name = "SERVER";
    hostParam[SERVER_INDEX].param_type = BOOL_PARAM;
    hostParam[SERVER_INDEX].index = -1;

    char *strKey=NULL;
    unsigned int numMapping=0, numHost=0;
    int index=-1;
    do {
        yaml_parser_scan(parser, &token);

        if (YAML_NO_ERROR != parser->error) {

            log_message(ERR, NOCLASS, "%s: error occurred when parsing host section of file(%s) at line %d: %s",
                        __func__, fname, parser->mark.line, parser->problem);

            free_param_value(hostParam);
            FREEUP(hostParam);
            quickpool_errno = ERROR_CONF_WARNING;
            return FALSE;
        }

        switch(token.type) {
        case YAML_FLOW_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_MAPPING_END_TOKEN:
            numMapping --;
            break;
        case YAML_FLOW_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_SEQUENCE_END_TOKEN:
            numMapping --;
            break;
        case YAML_BLOCK_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_END_TOKEN:
            numMapping --;
            break;
        case YAML_SCALAR_TOKEN:
            if (NULL == strKey) {
                index = match_param_key((char *)token.data.scalar.value, hostParam);
                if (index < 0) {
                    log_message(WARNING, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                                __func__, fname, parser->mark.line, (char *)token.data.scalar.value);
                    quickpool_errno = ERROR_CONF_WARNING;
                    break;
                }

                strKey = copy_string((char *)token.data.scalar.value);
            } else {

                if (0 == strcasecmp(strKey, hostParam[HOSTNAME_INDEX].param_name)) {
                    numHost ++;

                    if (numHost > 1) {

                        if (check_host_definition(hostParam, fname)) {
                            add_conf_host(hostParam, fname);
                        }

                        reset_param_value(hostParam);
                        for (i=0; i<qpinfo.num_index; i++) {

                            if (qpinfo.resource_table[i].order_type == INCR) {
                                hostParam[i].float_value = INFINIT_LOAD;
                            } else {
                                hostParam[i].float_value = -INFINIT_LOAD;
                            }
                        }
                    }
                }

                if (!set_param_value(index, (char *)token.data.scalar.value, hostParam)) {
                    log_message(ERR, NOCLASS, "%s: error occurred when parsing host section of file(%s) at line %d: see above for reason",
                                __func__, fname, parser->mark.line);
                    quickpool_errno = ERROR_CONF_WARNING;
                }

                index = -1;
                FREEUP(strKey);
            }

            break;
        default:
            break;
        }

        if ((token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN) || numMapping > 0) {
            yaml_token_delete(&token);
        }
    } while (token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN);
    yaml_token_delete(&token);

    if (numHost > 0 && check_host_definition(hostParam, fname)) {
        add_conf_host(hostParam, fname);
    }

    free_param_value(hostParam);
    FREEUP(hostParam);

    return TRUE;
} // end function parse_node

/**
 * @brief       Check syntax of resource map parameters.
 *
 * @param[in]   param         #1: resource map parameters
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parameter has syntax error
 */
static bool_t
check_location(struct config_param *param, char *fname)
{
    int resIndex;

    if (NULL == param[0].string_value) {
        log_message(ERR, NOCLASS, "%s: In %s ResourceMap section, no ResourceName specified.",
                    __func__, fname);

        return FALSE;
    }

    if ((resIndex = get_resource_index(param[0].string_value)) < 0) {
        log_message(ERR, NOCLASS, "%s: In %s ResourceMap section, resource <%s> is unknown.",
                    __func__, fname, param[0].string_value);

        return FALSE;
    } else {
        if (NUMBUILTIN > resIndex) {
            log_message(ERR, NOCLASS, "%s: In %s ResourceMap section, built-in resource %s can't be redefined as shared resource.",
                        __func__, fname, param[0].string_value);
            return FALSE;
        }
    }

    if (NULL == param[1].string_value) {
        log_message(ERR, NOCLASS, "%s: In %s ResourceMap section, no LOCATION specified for resource <%s>.",
                    __func__, fname, param[0].string_value);

        return FALSE;
    }

    int dynamic = (gptrSharedConf->qpinfo->resource_table[resIndex].flags & RESOURCE_FLAG_DYNAMIC);

    char *sp = param[1].string_value;

    unsigned int len;
    len = strlen(sp) + 5;

    char *strNewLoc = (char *)calloc(len+1, sizeof(char));
    if (NULL == strNewLoc) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", strlen(sp)+5);

        return FALSE;
    }

    bool_t allKeyword=FALSE, defaultKeyword=FALSE;
    while (*sp != '\0') {
        char *cp = strip_spaces(sp, FALSE);
        if (0 == strlen(cp)) {
            log_message(ERR, NOCLASS, "%s: In %s ResourceMap section, no valid LOCATION specified for resource <%s>.",
                        __func__, fname, param[0].string_value);

            FREEUP(strNewLoc);
            return FALSE;
        }

        sp = cp;

        if (*cp != '[' && *cp != '\0') {
            while (*cp && *cp != '@' && !(!iscntrl(*cp) && isspace(*cp))) {
                cp++;
            }
        }

        char *strLocValue=NULL;
        if (cp != sp) {
            char ssp = cp[0];
            cp[0] = '\0';
            if (!isdigit_string(sp)
                && gptrSharedConf->qpinfo->resource_table[resIndex].value_type == TYPE_NUMERIC) {

                log_message(ERR, NOCLASS, "%s: In %s ResourceMap section, invalid characters (%s) used as NUMERIC resource <%s> value.",
	                    __func__, fname, sp, param[0].string_value);
                cp[0] = ssp;
                FREEUP(strNewLoc);

                return FALSE;
            }

            strLocValue = copy_string(sp);
            snprintf(strNewLoc+strlen(strNewLoc), len, "%s", sp);

            cp[0] = ssp;

            while (isspace(*cp) && *cp != '\0') {
                cp++;
            }

            if (*cp != '@') {

                log_message(ERR, NOCLASS, "%s: In %s ResourceMap section, invalid characters (%c) specified, expect '@' after the static resource <%s> value.",
	                    __func__, fname, *cp, param[0].string_value);
                FREEUP(strNewLoc);
                FREEUP(strLocValue)

                return FALSE;
            }

            strncat(strNewLoc, "@", len);
            sp = cp + 1;
        }

        while (isspace(*sp) && *sp != '\0') {
            sp++;
        }

        if (*sp != '[') {
            log_message(ERR, NOCLASS,  "%s: In %s ResourceMap section, no <[> for resource <%s> instance in <%s>.",
                        __func__, fname, param[0].string_value, sp);
            FREEUP(strNewLoc);
            FREEUP(strLocValue)

            return FALSE;
        }

        if (*sp == '[') {
            char *strNode = get_string_inside(&sp, '[', ']');
            if (NULL == strNode) {
                log_message(ERR, NOCLASS, "%s: In %s ResourceMap section, number of '[' is not match that of ']' in <%s> for resource <%s>.",
                            __func__, fname, param[1].string_value, param[0].string_value);
                FREEUP(strNewLoc);
                FREEUP(strLocValue)

                return FALSE;
            }

            if (strLocValue == NULL && !dynamic) {
                log_message(ERR, NOCLASS, "%s: In %s ResourceMap section, value must be defined for static resource <%s> instance.",
                            __func__, fname, param[0].string_value);

                FREEUP(strLocValue)
                FREEUP(strNode);

                continue;
            }

            if (strLocValue != NULL && dynamic) {
               log_message(ERR, NOCLASS, "%s: In %s ResourceMap section, value <%s> ignored for dynamic resource <%s> instance.",
                           __func__, fname, strLocValue, param[0].string_value);
            }

            cp = strip_spaces(strNode, FALSE);
            unsigned int lenNode = strlen(strNode);
            char *node = (char *)calloc(lenNode+1, sizeof(char));
            if (NULL == node) {

                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", lenNode+1);
                FREEUP(strLocValue)
                FREEUP(strNode);

                return FALSE;
            }
            char *token;
            while ((token = get_string_token(&cp, " ", node, lenNode+1)) != NULL) {
                lenNode = strlen(cp);

                if (0 == strcmp(token, ALL_STRING)) {
                    allKeyword = TRUE;
                } else if (0 == strcmp(token, DEFAULT_STRING)) {
                    defaultKeyword = TRUE;
                }
            }
            FREEUP(node);

            if (allKeyword && defaultKeyword) {
               log_message(ERR, NOCLASS, "%s: In %s ResourceMap section, both 'all' and 'default' keywords are specified for resource <%s> instance, should remove one of them.",
                           __func__, fname, param[0].string_value);
                FREEUP(strLocValue)
                FREEUP(strNode);

                return FALSE;
            }

            snprintf(strNewLoc+strlen(strNewLoc), len, "[%s] ", strip_spaces(strNode, FALSE));
            FREEUP(strNode);
        }

        FREEUP(strLocValue)
    }

    strip_spaces(strNewLoc, FALSE);
    FREEUP(param[1].string_value);
    param[1].string_value = strNewLoc;

    return TRUE;
} // end function check_location

/**
 * @brief       Add resource map to cluster conf.
 *
 * @param[in]   parser        #1: yaml parser hanlder
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, not enough memory
 */
static bool_t
add_resource_map(struct config_param *param, char *fname)
{
    if (gptrClusterConf->num_location >= sintMaxLocation) {
        sintMaxLocation *= 2;
        gptrClusterConf->loc = (struct resourceLoc *)realloc(gptrClusterConf->loc,
                                                    sintMaxLocation*sizeof(struct resourceLoc));
        if (NULL == gptrClusterConf->loc) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "realloc", sintMaxLocation*sizeof(struct resourceLoc));
            quickpool_errno = ERROR_MEMORY;

            return FALSE;
        }
    }

    gptrClusterConf->loc[gptrClusterConf->num_location].resource_name = copy_string(param[0].string_value);
    gptrClusterConf->loc[gptrClusterConf->num_location].location = copy_string(param[1].string_value);
    gptrClusterConf->num_location ++;

    return TRUE;
} // end function add_resource_map

/**
 * @brief       Parse ResourceMap section in qp.cluster.xxx file.
 *
 * @param[in]   parser        #1: yaml parser hanlder
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 *
 * @note        parse, check and add
 */
static bool_t
parse_resource_map(yaml_parser_t *parser, char *fname)
{
    yaml_token_t token;

    yaml_parser_scan(parser, &token);
    if (token.type != YAML_VALUE_TOKEN) {
        yaml_token_delete(&token);

        log_message(ERR, NOCLASS, "%s: there is format error in ResourceMap section of file(%s) at line %d.",
                    __func__, fname, parser->mark.line);
        quickpool_errno = ERROR_CONF_WARNING;
        return FALSE;
    }
    yaml_token_delete(&token);

    struct config_param locationParam[] = {
        {"RESOURCENAME", STRING_PARAM, -1, {NULL}},
        {"LOCATION", STRING_PARAM, -1, {NULL}},
        {NULL, STRING_PARAM, -1, {NULL}}
    };

    char *strKey=NULL;
    unsigned int numMapping=0, numResMap=0;
    int index=-1;
    do {
        yaml_parser_scan(parser, &token);

        if (YAML_NO_ERROR != parser->error) {

            log_message(ERR, NOCLASS, "%s: error occurred when parsing ResourceMap section of file(%s) at line %d: %s",
                        __func__, fname, parser->mark.line, parser->problem);
            free_param_value(locationParam);
            quickpool_errno = ERROR_CONF_WARNING;
            return FALSE;
        }

        switch(token.type) {
        case YAML_FLOW_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_MAPPING_END_TOKEN:
            numMapping --;
            break;
        case YAML_FLOW_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_SEQUENCE_END_TOKEN:
            numMapping --;
            break;
        case YAML_BLOCK_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_END_TOKEN:
            numMapping --;
            break;
        case YAML_SCALAR_TOKEN:
            if (NULL == strKey) {
                index = match_param_key((char *)token.data.scalar.value, locationParam);
                if (index < 0) {
                    log_message(WARNING, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                                __func__, fname, parser->mark.line, (char *)token.data.scalar.value);
                    quickpool_errno = ERROR_CONF_WARNING;
                    break;
                }

                strKey = copy_string((char *)token.data.scalar.value);
            } else {
                if (0 == strcasecmp(strKey, locationParam[0].param_name)) {
                    numResMap ++;

                    if (numResMap > 1) {

                        if (check_location(locationParam, fname)) {
                            add_resource_map(locationParam, fname);
                        }
                        reset_param_value(locationParam);
                    }
                }

                if (!set_param_value(index, (char *)token.data.scalar.value, locationParam)) {
                    log_message(ERR, NOCLASS, "%s: error occurred when parsing ResourceMap section of file(%s) at line %d: see above for reason",
                                __func__, fname, parser->mark.line);
                    quickpool_errno = ERROR_CONF_WARNING;
                }

                index = -1;
                FREEUP(strKey);
            }

            break;
        default:
            break;
        }

        if ((token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN) || numMapping > 0) {
            yaml_token_delete(&token);
        }
    } while (token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN);
    yaml_token_delete(&token);

    if (numResMap > 0 && check_location(locationParam, fname)) {
        add_resource_map(locationParam, fname);
    }

    free_param_value(locationParam);

    return TRUE;
} // end function parse_resource_map

/**
 * @brief       Initialize limit of QuickPoolHostInfo. 
 *
 * @param[out]  hinfo      #3: host info to be initialized
 *
 */
static void
initialize_hostlimit(struct QuickPoolHostInfo *hinfo)
{
    if (NULL == hinfo) {
        return;
    }

    hinfo->status = INFINIT_INT;
    hinfo->user_slot_limit = INFINIT_INT;
    hinfo->max_jobs = INFINIT_INT;
    hinfo->num_job_slot = INFINIT_INT;
    hinfo->num_running_slot = INFINIT_INT;
    hinfo->num_SSUSP_slot = INFINIT_INT;
    hinfo->num_USUSP_slot = INFINIT_INT;
    hinfo->mig = INFINIT_INT;
    hinfo->attr = INFINIT_INT;

    return;
} // end function initialize_hostlimit

static void
read_threshold(struct quickpoolInfo *info, struct config_param *param,
               float loadsched[], float loadstop[], char *fname, const char *section)
{
    char *stop;
    float swap;
    unsigned int i;

    initialize_threshold(info->num_index, info->resource_table, loadsched, loadstop);

    for (i = 0; i < info->num_index; i++) {

        if (NULL == param[i].string_value) {
            continue;
        }

        if (strcmp(param[i].string_value, "") == 0) {
            continue;
        }

        if ((stop = strchr(param[i].string_value, '/')) != NULL ) {
            *stop = '\0';
            stop++;
            if (stop[0] == '\0') {
                stop = NULL;
            }
        }

        if (*param[i].string_value != '\0'
            && (loadsched[i] = gf_atof(param[i].string_value, INFINIT_LOAD,
                                       -INFINIT_LOAD)) >= INFINIT_LOAD) {
            log_message(ERR, NOCLASS, "%s: In %s%s, value <%s> of load schedule isn't a float number between %1.1f and %1.1f.",
                        __func__, fname, section, param[i].string_value, -INFINIT_LOAD, INFINIT_LOAD);
	    quickpool_errno = ERROR_CONF_WARNING;
            if (info->resource_table[i].order_type == DECR) {
                loadsched[i] = -INFINIT_LOAD;
            }
        }
        if (*param[i].string_value != '\0' && loadsched[i] < 0) {
            log_message(WARNING, NOCLASS, "%s: In %s%s, value <%s> of load schedule is not a non-negative number",
                        __func__, fname, section, param[i].string_value);
	    quickpool_errno = ERROR_CONF_WARNING;
        }

        if (i == UT) {
            if (loadsched[i] > 1.0 && loadsched[i] < INFINIT_LOAD) {
                log_message(WARNING, NOCLASS, "%s: In %s%s, for load index <UT>, load schedule <%.2f> is greater than 1.",
                            __func__, fname, section, loadsched[i]);
	        quickpool_errno = ERROR_CONF_WARNING;
                loadsched[i] /= 100.0;
            }
        }

        if (!stop) {
            continue;
        }

        if ((loadstop[i] = gf_atof(stop, INFINIT_LOAD, -INFINIT_LOAD)) == INFINIT_LOAD) {
            log_message(ERR, NOCLASS, "%s: In %s%s, value <%s> of load stop isn't a float number between %1.1f and %1.1f; ignored",
                        __func__, fname, section, stop, -INFINIT_LOAD, INFINIT_LOAD);
	    quickpool_errno = ERROR_CONF_WARNING;
            if (info->resource_table[i].order_type == DECR) {
                loadstop[i] = -INFINIT_LOAD;
            }

            continue;
        }

        if (loadstop[i] < 0) {
            log_message(WARNING, NOCLASS, "%s: In %s%s, value <%s> of load stop is not a non-negative number",
                        __func__, fname, section, stop);
	    quickpool_errno = ERROR_CONF_WARNING;
        }

        if (i == UT) {
            if (loadstop[i] > 1.0 && loadstop[i] < INFINIT_LOAD) {
                log_message(WARNING, NOCLASS, "%s: In %s%s, for load index <UT>, loadstop <%.2f> is greater than 1",
                            __func__, fname, section, loadstop[i]);
	        quickpool_errno = ERROR_CONF_WARNING;
                loadstop[i] /= 100.0;
            }
        }

        if (info->resource_table[i].order_type == INCR) {

            if (loadsched[i] > loadstop[i]) {
                log_message(ERR, NOCLASS, "%s: In %s%s, for load index <%s>, load stop <%2.2f> is lower than load schedule <%2.2f>.",
                            __func__, fname, section, info->resource_table[i].name,
                            loadstop[i], loadsched[i]);
                quickpool_errno = ERROR_CONF_WARNING;
                swap = loadsched[i];
                loadsched[i] = loadstop[i];
                loadstop[i] = swap;
            }
        } else if (info->resource_table[i].order_type == DECR) {

            if (loadstop[i] > loadsched[i]) {
                log_message(ERR, NOCLASS, "%s: In %s%s, for load index <%s>, load stop <%2.2f> is higher than load schedule <%2.2f>; swapped",
                            __func__, fname, section, info->resource_table[i].name,
                            loadstop[i], loadsched[i]);
                quickpool_errno = ERROR_CONF_WARNING;
                swap = loadsched[i];
                loadsched[i] = loadstop[i];
                loadstop[i] = swap;
            }
        }
    }

    return;
} // end function read_threshold

/**
 * @brief       parse node name with [] and put names into link. 
 *
 * @param[in]   name          #1: host name
 * @param[in]   namelist      #2: host name list
 *
 * @retval      >0            #1: succeed, number of node name
 * @retval      0             #2: failed, format error
 *
 * @note        host[1-10]: host1, host2 ... host 10 are in name list.
 */
static int
node_base_name(const char *name, LIST_T *namelist)
{
    unsigned int len = strlen(name);
    char *basename = (char *)calloc(len+1, sizeof(char));
    if (NULL == basename) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
        return 0;
    }
    char *token;
    while (NULL != (token = get_string_token((char **)&name, " ", basename, len+1))) {
        len = strlen(name);

        if (!strchr(token, '[') || !strchr(token, ']') || !strchr(token, '-')) {

            LIST_PROXY_T *pxy;
            pxy = (LIST_PROXY_T *)calloc(1, sizeof(struct list_proxy));
            if (NULL == pxy) {
                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                            "calloc", sizeof(struct list_proxy));
                FREEUP(basename);
                return 0;
            }

            pxy->subject = (void *)copy_string(token);
            gf_list_insert(namelist, namelist, (LIST_T *)pxy);

            continue;
        }

        /* get the hostname */
        char *p;
        p = strchr(token, '[');
        if (NULL == p) {
	    log_message(ERR, NOCLASS, "%s: mismatch [-] in node name list <%s>.", __func__, token);
            FREEUP(basename);
	    return 0;
        }

        *p = 0;
        ++p;

        char name2[MAXHOSTNAMELEN+1];
        strncpy(name2, token, MAXHOSTNAMELEN);

        int i;
        for (i=0; p[i]!=0; i++) {
            if (p[i] == ']' || p[i] == '-') {
		p[i] = ' ';
            }
	}

	/* Get the name index */
        int cc, low, high;
	cc = sscanf(p, "%d%d", &low, &high);
	if (cc != 2) {
	    log_message(ERR, NOCLASS, "%s: unrecognized format of node name <%s>.", __func__, token);
            FREEUP(basename);
	    return 0;
	}

        if (low > high) {
	    log_message(ERR, NOCLASS, "%s: lower index <%d> of node name <%s> is larger than higher index <%d>.",
                        __func__, low, token, high);
            FREEUP(basename);
	    return 0;
        }

	for (i=low; i<=high; i++) {

            char buf[MAXHOSTNAMELEN+1];
	    snprintf(buf, MAXHOSTNAMELEN, "%s%d", name2, i);

            LIST_PROXY_T *pxy;
            pxy = (LIST_PROXY_T *)calloc(1, sizeof(struct list_proxy));
            if (NULL == pxy) {
                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                            "calloc", sizeof(struct list_proxy));
                FREEUP(basename);
                return 0;
            }

            pxy->subject = (void *)copy_string(buf);
            gf_list_insert(namelist, namelist, (LIST_T *)pxy);
	}
    }

    FREEUP(basename);
    return LIST_NUM_ENTS(namelist);
} // end function name_base_name

/**
 * @brief       Check JS node defined parameters.
 *
 * @param[in]   param         #1: JS node parameters
 * @param[in]   fname         #2: full path file name
 * @param[in]   info          #3: cluster resource table
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parameter has syntax error
 */
static bool_t
check_jsnode_definition(struct config_param *param, char *fname, struct quickpoolInfo *info)
{
#define JSNODE_HOSTNAME_INDEX    info->num_index+0
#define JSNODE_MXJ_INDEX         info->num_index+1
#define JSNODE_MIG_INDEX         info->num_index+2
#define JSNODE_UJOBLIMIT_INDEX   info->num_index+3
#define JSNODE_DISPATCHWIN_INDEX info->num_index+4

    if (NULL == param[JSNODE_HOSTNAME_INDEX].string_value) {
        log_message(ERR, NOCLASS, "%s: In %s host section, host name must be specified for a host definition.",
                    __func__, fname);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    if (0 != strcmp(param[JSNODE_HOSTNAME_INDEX].string_value, DEFAULT_STRING)) {

        LIST_T *nodeNames = gf_list_make("node name list");

        int numNode;
        numNode = node_base_name(param[JSNODE_HOSTNAME_INDEX].string_value, nodeNames);
        if (0 >= numNode) {

            gf_list_free(nodeNames, gf_free_proxy_string);
            quickpool_errno = ERROR_CONF_WARNING;
            return FALSE;
        }

        LIST_ITERATOR_T iter;
        gf_listiterator_link(&iter, nodeNames);

        LIST_PROXY_T *pxy;
        for (pxy = (LIST_PROXY_T *)gf_listiterator_curentry(&iter);
             !gf_listiterator_isend(&iter);
             gf_listiterator_getnext(&iter, (LIST_T **)&pxy)) {

            char *nodeName;
            nodeName = (char *)pxy->subject;
            if (NULL == gf_hash_find(shashHost, nodeName)) {

                unsigned int i;
                for (i = 0; i <info->num_models; i++) {
                    if (strcmp(nodeName, info->host_models[i]) == 0) {
                        break;
                    }
                }

                if (i == info->num_models) {
                    for (i = 0; i <info->num_types; i++) {
                        if (strcmp(nodeName, info->host_types[i]) == 0) {
                            break;
                        }
                    }

                    if (i == info->num_types) {

                        log_message(ERR, NOCLASS, "%s: In %s host section: node name <%s> is not defined in qp.cluster file.",
                                    __func__, fname, nodeName);

                        gf_list_free(nodeNames, gf_free_proxy_string);
                        quickpool_errno = ERROR_CONF_WARNING;

                        return FALSE;
                    }
                }
            }
        } // end for (pxy)

        gf_list_free(nodeNames, gf_free_proxy_string);
    }

    if (NULL != param[JSNODE_MXJ_INDEX].string_value) {
        if (strcmp(param[JSNODE_MXJ_INDEX].string_value, "!") != 0) {
            if (gf_atoi(param[JSNODE_MXJ_INDEX].string_value,INFINIT_INT,0) == INFINIT_INT) {
                log_message(ERR, NOCLASS, "%s: In %s host section, invalid MXJ value <%s>, should be integer between 0 and 2147483646.",
                            __func__, fname, param[JSNODE_MXJ_INDEX].string_value);

                quickpool_errno = ERROR_CONF_WARNING;
            }
        }
    }

    if (0 < param[JSNODE_UJOBLIMIT_INDEX].index) {
        if (param[JSNODE_UJOBLIMIT_INDEX].int_value < 0) {
            log_message(ERR, NOCLASS, "%s: In %s host section, invalid value <%d> for JL/U, must be larger than 0.",
                        __func__, fname, param[JSNODE_UJOBLIMIT_INDEX].int_value);

            param[JSNODE_UJOBLIMIT_INDEX].int_value = INFINIT_INT;
            quickpool_errno = ERROR_CONF_WARNING;
        }
    }

    if (0 < param[JSNODE_MIG_INDEX].index) {
        if (param[JSNODE_MIG_INDEX].int_value < 0
            || param[JSNODE_MIG_INDEX].int_value > INFINIT_INT/60) {

            log_message(ERR, NOCLASS, "%s: In %s host section, invalid value <%d> for MIG, must be larger than 0 and smaller than %d.",
                        __func__, fname, param[JSNODE_UJOBLIMIT_INDEX].int_value, INFINIT_INT/60);

            param[JSNODE_MIG_INDEX].int_value = INFINIT_INT;
            quickpool_errno = ERROR_CONF_WARNING;
        }
    }

    return TRUE;
} // end function check_jsnode_definition

/**
 * @brief       Update host information.
 *
 * @param[in]   hostinfo      #1: host info to be updated
 * @param[in]   param         #2: JS node parameters
 * @param[in]   fname         #3: full path file name
 * @param[in]   info          #4: cluster resource table
 */
static void
update_hostinfo(struct QuickPoolHostInfo *hostinfo, struct config_param *param, char *fname,
                struct quickpoolInfo *info)
{
    if (NULL == param[JSNODE_MXJ_INDEX].string_value
        || strcmp(param[JSNODE_MXJ_INDEX].string_value, "!") == 0) {
        hostinfo->max_jobs = -1;
    } else {
        hostinfo->max_jobs = atoi(param[JSNODE_MXJ_INDEX].string_value);
        if (hostinfo->max_jobs < 0) {
            hostinfo->max_jobs = -1;
        }
    }

    hostinfo->mig = param[JSNODE_MIG_INDEX].int_value;
    hostinfo->user_slot_limit = param[JSNODE_UJOBLIMIT_INDEX].int_value;

    if (0 < param[JSNODE_DISPATCHWIN_INDEX].index
        && NULL != param[JSNODE_DISPATCHWIN_INDEX].string_value) {

        hostinfo->dispatch_window = parse_time_window(param[JSNODE_DISPATCHWIN_INDEX].string_value,
                                                      NULL, "host");
        if (NULL == hostinfo->dispatch_window) {
            quickpool_errno = ERROR_CONF_WARNING;
        }
    }

    if (NULL == hostinfo->load_schedule && info->num_index > 0) {
        hostinfo->load_schedule = (float *)calloc(info->num_index, sizeof(float));
        if (NULL == hostinfo->load_schedule) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", (info->num_index)*sizeof(float));
            quickpool_errno = ERROR_MEMORY;
        }
    }
    if (NULL == hostinfo->load_stop && info->num_index > 0) {
        hostinfo->load_stop = (float *)calloc(info->num_index, sizeof(float));
        if (NULL == hostinfo->load_stop) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", (info->num_index)*sizeof(float));
            quickpool_errno = ERROR_MEMORY;
        }
    }

    read_threshold(info, param, hostinfo->load_schedule, hostinfo->load_stop,
                   fname, " host section");

    return;
} // end function update_hostinfo

static void 
free_hostinfo(struct QuickPoolHostInfo *hostinfo)
{
    if (NULL == hostinfo) {
        return;
    }

    FREEUP(hostinfo->dispatch_window);
    FREEUP(hostinfo->load_schedule);
    FREEUP(hostinfo->load_stop);
    FREEUP(hostinfo);

    return;
}

/**
 * @brief       Update JS node information.
 *
 * @param[in]   param         #1: JS node parameters
 * @param[in]   fname         #2: full path file name
 * @param[in]   info          #3: cluster resource table
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, not enough memory
 */
static bool_t
update_jsnode(struct config_param *param, char *fname, struct quickpoolInfo *info)
{
    struct QuickPoolHostInfo *hostInfo;

    if (0 == strcmp(param[JSNODE_HOSTNAME_INDEX].string_value, DEFAULT_STRING)) {
        sptrDefaultHost = (struct QuickPoolHostInfo *)calloc(1, sizeof(struct QuickPoolHostInfo));
        if (NULL == sptrDefaultHost) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", (info->num_index)*sizeof(float));
            quickpool_errno = ERROR_MEMORY;

            return FALSE;
        }

        update_hostinfo(sptrDefaultHost, param, fname, info);

        return TRUE;
    }

    LIST_T *nodeNames = gf_list_make("node name list");

    node_base_name(param[JSNODE_HOSTNAME_INDEX].string_value, nodeNames);

    LIST_ITERATOR_T iter;
    gf_listiterator_link(&iter, nodeNames);

    LIST_PROXY_T *pxy;
    for (pxy = (LIST_PROXY_T *)gf_listiterator_curentry(&iter);
         !gf_listiterator_isend(&iter);
         gf_listiterator_getnext(&iter, (LIST_T **)&pxy)) {

        char *nodeName = (char *)pxy->subject;
        hostInfo = (struct QuickPoolHostInfo *)gf_hash_find(shashHost, nodeName);
        if (NULL != hostInfo) {
            update_hostinfo(hostInfo, param, fname, info);
            continue;
        }

        unsigned int i;
        for (i = 0; i <info->num_models; i++) {
            if (strcmp(nodeName, info->host_models[i]) == 0) {
                break;
            }
        }

        if (i < info->num_models) {

            HASH_WALK_T walk;
            char *key;
            gf_hash_walk_start(shashHost, &walk);
            while (NULL != (hostInfo = (struct QuickPoolHostInfo *)gf_hash_walk(&walk, &key))) {
                if (NULL == hostInfo->host_model) {
                    continue;
                }

                if (strcmp(hostInfo->host_model, info->host_models[i]) != 0) {
                    continue;
                }

                update_hostinfo(hostInfo, param, fname, info);
            }
            gf_hash_walk_end(&walk);

            continue;
        }

        for (i = 0; i <info->num_types; i++) {
            if (strcmp(nodeName, info->host_types[i]) == 0) {
                break;
            }
        }

        if (i < info->num_types) {

            HASH_WALK_T walk;
            char *key;
            gf_hash_walk_start(shashHost, &walk);
            while (NULL != (hostInfo = (struct QuickPoolHostInfo *)gf_hash_walk(&walk, &key))) {
                if (NULL == hostInfo->host_type) {
                    continue;
                }

                if (strcmp(hostInfo->host_type, info->host_types[i]) != 0) {
                    continue;
                }

                update_hostinfo(hostInfo, param, fname, info);
            }
            gf_hash_walk_end(&walk);

            continue;
        }
    }

    gf_list_free(nodeNames, gf_free_proxy_string);

    return TRUE;
} // end function update_jsnode

/**
 * @brief       Parse Host section in qp.hosts file.
 *
 * @param[in]   parser        #1: yaml parser hanlder
 * @param[in]   fname         #2: full path file name
 * @param[in]   info          #3: cluster resource table
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 *
 * @note        parse, check and update js node info. JS node info comes from
 *              JM, no new node should be added in this function.
 */
static bool_t
parse_jsnode(yaml_parser_t *parser, char *fname, struct quickpoolInfo *info)
{
    yaml_token_t token;

    yaml_parser_scan(parser, &token);
    if (token.type != YAML_VALUE_TOKEN) {
        yaml_token_delete(&token);

        log_message(ERR, NOCLASS, "%s: there is format error in host section of file(%s) at line %d.",
                    __func__, fname, parser->mark.line);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }
    yaml_token_delete(&token);

    struct config_param *hostParam;
    hostParam = (struct config_param *)calloc(info->num_index + 6, sizeof(struct config_param));
    if (NULL == hostParam) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", (info->num_index+6)*sizeof(struct config_param));
        quickpool_errno = ERROR_MEMORY;

        return FALSE;
    }

    unsigned int i;
    for (i=0; i<info->num_index; i++) {

        hostParam[i].param_name = info->resource_table[i].name;
        hostParam[i].param_type = STRING_PARAM;
        hostParam[i].index = -1;
    }

    hostParam[JSNODE_HOSTNAME_INDEX].param_name = "HOST_NAME";
    hostParam[JSNODE_HOSTNAME_INDEX].param_type = STRING_PARAM;
    hostParam[JSNODE_HOSTNAME_INDEX].index = -1;
    hostParam[JSNODE_MXJ_INDEX].param_name = "MXJ";
    hostParam[JSNODE_MXJ_INDEX].param_type = STRING_PARAM;
    hostParam[JSNODE_MXJ_INDEX].index = -1;
    hostParam[JSNODE_MIG_INDEX].param_name = "MIG";
    hostParam[JSNODE_MIG_INDEX].param_type = INT_PARAM;
    hostParam[JSNODE_MIG_INDEX].index = -1;
    hostParam[JSNODE_UJOBLIMIT_INDEX].param_name = "UJOB_LIMIT";
    hostParam[JSNODE_UJOBLIMIT_INDEX].param_type = INT_PARAM;
    hostParam[JSNODE_UJOBLIMIT_INDEX].index = -1;
    hostParam[JSNODE_DISPATCHWIN_INDEX].param_name = "DISPATCH_WINDOW";
    hostParam[JSNODE_DISPATCHWIN_INDEX].param_type = STRING_PARAM;
    hostParam[JSNODE_DISPATCHWIN_INDEX].index = -1;

    hostParam[JSNODE_MIG_INDEX].int_value = INFINIT_INT;
    hostParam[JSNODE_UJOBLIMIT_INDEX].int_value = INFINIT_INT;

    char *strKey=NULL;
    unsigned int numMapping=0, numHost=0;
    int index=-1;
    do {
        yaml_parser_scan(parser, &token);

        if (YAML_NO_ERROR != parser->error) {

            log_message(ERR, NOCLASS, "%s: error occurred when parsing host section of file(%s) at line %d: %s",
                        __func__, fname, parser->mark.line, parser->problem);

            free_param_value(hostParam);
            FREEUP(hostParam);
            quickpool_errno = ERROR_CONF_WARNING;

            return FALSE;
        }

        switch(token.type) {
        case YAML_FLOW_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_MAPPING_END_TOKEN:
            numMapping --;
            break;
        case YAML_FLOW_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_SEQUENCE_END_TOKEN:
            numMapping --;
            break;
        case YAML_BLOCK_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_END_TOKEN:
            numMapping --;
            break;
        case YAML_SCALAR_TOKEN:
            if (NULL == strKey) {
                index = match_param_key((char *)token.data.scalar.value, hostParam);
                if (index < 0) {
                    log_message(WARNING, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                                __func__, fname, parser->mark.line, (char *)token.data.scalar.value);
                    quickpool_errno = ERROR_CONF_WARNING;
                    break;
                }

                strKey = copy_string((char *)token.data.scalar.value);
            } else {

                if (0 == strcasecmp(strKey, hostParam[JSNODE_HOSTNAME_INDEX].param_name)) {
                    numHost ++;

                    if (numHost > 1) {

                        if (check_jsnode_definition(hostParam, fname, info)) {
                            update_jsnode(hostParam, fname, info);
                        }

                        reset_param_value(hostParam);

                        hostParam[JSNODE_MIG_INDEX].int_value = INFINIT_INT;
                        hostParam[JSNODE_UJOBLIMIT_INDEX].int_value = INFINIT_INT;
                    }
                }

                if (!set_param_value(index, (char *)token.data.scalar.value, hostParam)) {
                    log_message(ERR, NOCLASS, "%s: error occurred when parsing host section of file(%s) at line %d: see above for reason",
                                __func__, fname, parser->mark.line);
                    quickpool_errno = ERROR_CONF_WARNING;
                }

                index = -1;
                FREEUP(strKey);
            }

            break;
        default:
            break;
        }

        if ((token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN) || numMapping > 0) {
            yaml_token_delete(&token);
        }
    } while (token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN);
    yaml_token_delete(&token);

    if (numHost > 0 && check_jsnode_definition(hostParam, fname, info)) {
        update_jsnode(hostParam, fname, info);
    }

    free_param_value(hostParam);
    FREEUP(hostParam);

    return TRUE;
} // end function parse_jsnode

/**
 * @brief       Add OS group and its member to cluster user group.
 *
 * @param[in]   osgrp         #1: OS group
 * @param[in]   gname         #2: user group name
 * @param[in]   fname         #3: full path file name
 * @param[in]   section       #4: section name
 *
 * @retval      not NULL      #1: succeed, OS group is put into my cluster
 * @retval      NULL          #2: failed, not enough memory or reach limit
 */
static struct QuickPoolUserGroupInfo *
add_os_grp(struct group *osgrp, char *gname, const char *fname, const char *section)
{
    if (NULL == osgrp) {
        return NULL;
    }

    if (sptrUserConf->num_user_groups >= MAX_GROUPS) {
        log_message(ERR, NOCLASS, "%s: In %s%s, the number of configured user groups reaches the limit <%d>",
                    __func__, fname, section, MAX_GROUPS);
        return NULL;
    }

    if (NULL == gname) {
        gname = osgrp->gr_name;
    }

    if (NULL == gname) {
        return NULL;
    }

    if (NULL == osgrp->gr_mem) {
        log_message(ERR, NOCLASS, "%s: In %s%s, the os user group <%s> has no member.",
                    __func__, fname, section, osgrp->gr_name);
        return NULL;
    }

    struct QuickPoolUserGroupInfo *userGroup=NULL;
    if (sptrUserConf->num_user_groups == 0) {
        sptrUserConf->user_groups = (struct QuickPoolUserGroupInfo *)calloc(1, sizeof(struct QuickPoolUserGroupInfo));
        if (NULL == sptrUserConf->user_groups) {

            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sizeof(struct QuickPoolUserGroupInfo));
            quickpool_errno = ERROR_MEMORY;

            return NULL;
        }

        userGroup = &(sptrUserConf->user_groups[0]);
    } else {
        sptrUserConf->user_groups = (struct QuickPoolUserGroupInfo *)realloc(sptrUserConf->user_groups, (sptrUserConf->num_user_groups+1)*sizeof(struct QuickPoolUserGroupInfo));
        if (NULL == sptrUserConf->user_groups) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "realloc",
                        (sptrUserConf->num_user_groups+1)*sizeof(struct QuickPoolUserGroupInfo));
            quickpool_errno = ERROR_MEMORY;

            return NULL;
        }

        userGroup = &(sptrUserConf->user_groups[sptrUserConf->num_user_groups]);
        memset(userGroup, 0, sizeof(struct QuickPoolUserGroupInfo));
    }

    sptrUserConf->num_user_groups ++;
    userGroup->group_name = copy_string(gname);
    userGroup->member_list = (struct nameList *)calloc(1, sizeof(struct nameList));
    if (NULL == userGroup->member_list) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct nameList));
        quickpool_errno = ERROR_MEMORY;

        return NULL;
    }

    int i = -1;
    while (NULL != osgrp->gr_mem[++i])  {

        if (NULL != get_user_byname(osgrp->gr_mem[i])) {
            if (0 > add_member_2namelist(osgrp->gr_mem[i], userGroup->member_list)) {
                quickpool_errno = ERROR_MEMORY;
                return NULL;
            }
        } else {
            log_message(ERR, NOCLASS, "%s: In %s%s, bad user name <%s> in group <%s>.",
                        __func__, fname, section, osgrp->gr_mem[i], gname);
            quickpool_errno = ERROR_CONF_WARNING;
            continue;
        }
    }

    return (userGroup);
} // end function add_os_grp

static struct QuickPoolHostGroupInfo *
get_hostgroup(char *gname)
{
    if (NULL == gname || NULL == sptrHostConf || 0 == sptrHostConf->num_host_groups) {
        return NULL;
    }

    unsigned int i;
    for (i = 0; i < sptrHostConf->num_host_groups; i++) {

        if (NULL == sptrHostConf->host_groups[i].group_name) {
            continue;
        }

        if (0 == strcmp(gname, sptrHostConf->host_groups[i].group_name)) {
            return (&(sptrHostConf->host_groups[i]));
        }
    }

    return NULL;
} // end function get_hostgroup

static struct QuickPoolUserGroupInfo *
get_usergroup(char *gname)
{
    if (NULL == gname || NULL == sptrUserConf || 0 == sptrUserConf->num_user_groups) {
        return NULL;
    }

    unsigned int i;
    for (i = 0; i < sptrUserConf->num_user_groups; i++) {
        if (NULL == sptrUserConf->user_groups[i].group_name) {
            continue;
        }

        if (strcmp(gname, sptrUserConf->user_groups[i].group_name) == 0) {
            return (&(sptrUserConf->user_groups[i]));
        }
    }

    return NULL;
} // end function get_usergroup

/**
 * @brief       check if node is a server in my cluster.
 *
 * @param[in]   name          #1: node name to be checked
 *
 * @retval      TRUE          #1: is server
 * @retval      FALSE         #2: not server
 */
static bool_t
is_server(char *name)
{
    unsigned int i;

    if (NULL == name || 0 >= gptrClusterConf->num_hosts) {
        return FALSE;
    }

    for (i = 0; i < gptrClusterConf->num_hosts; i++) {
        if (!issame_host(gptrClusterConf->hosts[i].host, name)) {
            continue;
        }

        if (gptrClusterConf->hosts[i].server != CLIENT) {
            return TRUE;
        }

        break;
    }

    return FALSE;
} // end function is_server

static int
parse_first_node(char *nodename, bool_t *first, const char *fname, const char *section)
{
    int len;

    len = strlen(nodename);
    if (nodename[len-1] != FIRST_HOST_TOKEN) {
        return 0;
    }

    nodename[len-1] = '\0';
    if (0 == strcmp(nodename, "others")) {
        log_message(ERR, NOCLASS, "%s: In %s%s, \"others\" specified as first execution host.",
                    __func__, fname, section);
        nodename[len-1] = '!';
        quickpool_errno = ERROR_CONF_WARNING;
        return 1;
    }

    if (0 == strcmp(nodename, ALL_STRING)) {
        log_message(ERR, NOCLASS, "%s: In %s%s, \"all\" specified as first execution host.",
                    __func__, fname, section);
        nodename[len-1] = '!';
        quickpool_errno = ERROR_CONF_WARNING;
        return 1;
    }

    struct QuickPoolHostGroupInfo *group;
    group = get_hostgroup(nodename);
    if (NULL != group) {
        log_message(ERR, NOCLASS, "%s: In %s%s, host group <%s> specified as first execution host.",
                    __func__, fname, section, nodename);
        nodename[len-1] = '!';
        quickpool_errno = ERROR_CONF_WARNING;
        return 1;
    }

    struct hostent *hent;
    hent = gf_gethostbyname(nodename);
    if (NULL == hent) {
        log_message(ERR, NOCLASS, "%s: In %s%s, exclude a bad node/node group member <%s>. The member is ignored.",
                    __func__, fname, section, nodename);
        nodename[len-1] = '!'; 
        quickpool_errno = ERROR_CONF_WARNING;
        return 1;
    }
    
    if (!is_server(hent->h_name)) {
        log_message(ERR, NOCLASS, "%s: In %s%s, exclude a member <%s> who is not a server. The member is ignored.",
                            __func__, fname, section, hent->h_name);
        
        nodename[len-1] = '!'; 
        quickpool_errno = ERROR_CONF_WARNING;
        return -1;
    }

    nodename[len-1] = '!';
    if (*first) {
        log_message(ERR, NOCLASS, "%s: In %s%s, Multiple first execution hosts specified: <%s>.",
                    __func__, fname, section, nodename);
        quickpool_errno = ERROR_CONF_WARNING;
        return 1;
    }

    *first = TRUE;

    return 0;
} // end function parse_first_node

static int
parse_nodegroup_member(char *members, struct QuickPoolHostGroupInfo *groupinfo,
                       const char *fname, const char *section)
{
    if (NULL == groupinfo || NULL == members) {
        log_message(ERR, NOCLASS, "%s: input members or groupinfo is NULL.", __func__);
        quickpool_errno = ERROR_ARGUMENT;
        return FALSE;
    }

    struct nameList *groupMember;
    groupMember = longstring_2namelist(members);
    if (NULL == groupMember) {
        log_message(ERR, NOCLASS, STR_FUNC_S_FAIL, __func__,
                    "longstring_2namelist", "calloc");
        quickpool_errno = ERROR_MEMORY;
        return -1;
    }

    struct nameList *basenameList;
    basenameList = new_namelist(20);
    if (NULL == basenameList) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", sizeof(struct nameList));
        quickpool_errno = ERROR_MEMORY;
        return -1;
    }

    char *strIgnoreMember;

    bool_t firstNode = FALSE;

    unsigned int i;
    for (i=0; i<groupMember->num_name; i++) {

        char *strMember;
        strMember = groupMember->names[i];

        if ('~' == strMember[0]) {

            strMember ++;
            if (0 == strlen(strMember)) {
                log_message(ERR, NOCLASS, "%s: In %s%s, nothing specified after '~' for group <%s>.",
                            __func__, fname, section, groupinfo->group_name);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;            
                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }

            struct hostent *hent;
            hent = gf_gethostbyname(strMember);
            if (NULL == hent) {
                log_message(ERR, NOCLASS, "%s: In %s%s, exclude a bad node/node group member <%s> from group <%s>. The member is ignored.",
                            __func__, fname, section, strMember, groupinfo->group_name);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;            
                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }

            if (!is_server(hent->h_name)) {

                log_message(ERR, NOCLASS, "%s: In %s%s, exclude a member <%s> who is not a server in the cluster from group <%s>. The member is ignored.",
                            __func__, fname, section, hent->h_name, groupinfo->group_name);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;            
                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }

            if (NULL == groupinfo->exclude_members) {
                groupinfo->exclude_members = copy_string(strMember);
            } else {

                char *strNewMembers;
                unsigned int length;
                length = strlen(groupinfo->exclude_members) + strlen(strMember) + 2;
                strNewMembers = (char *)realloc(groupinfo->exclude_members, length+1);
                if (NULL == strNewMembers) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "realloc", length);
                    quickpool_errno = ERROR_MEMORY;
                    return -1;
                }

                unsigned int lenExMember;
                lenExMember = strlen(groupinfo->exclude_members);
                groupinfo->exclude_members = strNewMembers;
                snprintf(groupinfo->exclude_members+lenExMember, length, " %s", strMember);
            }

            strIgnoreMember = groupMember->names[i];
            FREEUP(strIgnoreMember);
            groupMember->names[i] = groupMember->names[groupMember->num_name-1];
            groupMember->names[groupMember->num_name-1] = NULL;
            groupMember->num_name --;
            i --;            

            continue;
        }

        if ('[' == strMember[0] || '-' == strMember[0] || ']' == strMember[0]) {
            log_message(ERR, NOCLASS, "%s: In %s%s, node name <%s> cannot start with [, -, or ].",
                        __func__, fname, section, strMember);

            strIgnoreMember = groupMember->names[i];
            FREEUP(strIgnoreMember);
            groupMember->names[i] = groupMember->names[groupMember->num_name-1];
            groupMember->names[groupMember->num_name-1] = NULL;
            groupMember->num_name --;
            i --;            
            quickpool_errno = ERROR_CONF_WARNING;

            continue;
        }

        if (0 == strcmp(groupMember->names[i], ALL_STRING)) {
            break;
        }

        if (parse_first_node(groupMember->names[i], &firstNode, fname, section)) {

            strIgnoreMember = groupMember->names[i];
            FREEUP(strIgnoreMember);
            groupMember->names[i] = groupMember->names[groupMember->num_name-1];
            groupMember->names[groupMember->num_name-1] = NULL;
            groupMember->num_name --;
            i --;            
            quickpool_errno = ERROR_CONF_WARNING;

            continue;
        }

        char *strSubGroup=NULL;

        int splash;
        splash = strlen(groupMember->names[i]) - 1;
        if (0 < splash && '/' == groupMember->names[i][splash]) {

            strSubGroup = copy_string(groupMember->names[i]);
            strSubGroup[splash] = '\0';
        } else {
            strSubGroup = copy_string(groupMember->names[i]);
            splash = 0;
        }

        struct QuickPoolHostGroupInfo *subNodeGroup;
        subNodeGroup = get_hostgroup(strSubGroup);

        if (groupinfo == subNodeGroup) {

            log_message(ERR, NOCLASS, "%s: In %s%s, recursive member defined in group <%s>. The member is ignored.",
                        __func__, fname, section, groupinfo->group_name);

            strIgnoreMember = groupMember->names[i];
            FREEUP(strIgnoreMember);
            groupMember->names[i] = groupMember->names[groupMember->num_name-1];
            groupMember->names[groupMember->num_name-1] = NULL;
            groupMember->num_name --;
            i --;

            FREEUP(strSubGroup);
            quickpool_errno = ERROR_CONF_WARNING;

            continue;
        } else if (NULL != subNodeGroup) { // cluster node group

            FREEUP(strSubGroup);
            if (0 == splash) {
                unsigned int len;
                len = strlen(groupMember->names[i])+2;

                strSubGroup = (char *)calloc(len+1, sizeof(char));
                if (NULL == strSubGroup) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                "calloc", strlen(groupMember->names[i])+2);
                    quickpool_errno = ERROR_MEMORY;
                    return -1;
                }

                snprintf(strSubGroup, len, "%s/", groupMember->names[i]);
                FREEUP(groupMember->names[i]);
                groupMember->names[i] = strSubGroup;
            }

            continue;
        }

        if (NULL != strchr(strSubGroup, '[') && NULL != strchr(strSubGroup, ']')
            && NULL != strchr(strSubGroup, '-')) {

            LIST_T *nodeNames;
            nodeNames = gf_list_make("node name list");

            int numNode;
            numNode = node_base_name(strSubGroup, nodeNames);
            if (0 == numNode) { // syntax error, ignore the group member

                log_message(ERR, NOCLASS, "%s: In %s%s, node names <%s> has syntax error. The member is ignored.",
			    __func__, fname, section, strSubGroup);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;

                FREEUP(strSubGroup);
                gf_list_free(nodeNames, gf_free_proxy_string);
                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }

            LIST_ITERATOR_T iter;
            gf_listiterator_link(&iter, nodeNames);

            LIST_PROXY_T *pxy;
            for (pxy = (LIST_PROXY_T *)gf_listiterator_curentry(&iter);
                 !gf_listiterator_isend(&iter);
                 gf_listiterator_getnext(&iter, (LIST_T **)&pxy)) {

                char *nodeName = (char *)pxy->subject;

                struct hostent *hent;
                hent = gf_gethostbyname(nodeName);
                if (NULL == hent) {

                    log_message(ERR, NOCLASS, "%s: In %s%s, exclude a bad node/node group member <%s> from group <%s>. The member is ignored.",
                                __func__, fname, section, strMember, groupinfo->group_name);
                    quickpool_errno = ERROR_CONF_WARNING;

                    continue;
                }

                if (!is_server(hent->h_name)) {

                    log_message(ERR, NOCLASS, "%s: In %s%s, exclude a member <%s> who is not a server in the cluster from group <%s>. The member is ignored.",
                                __func__, fname, section, hent->h_name, groupinfo->group_name);
                    quickpool_errno = ERROR_CONF_WARNING;

                    continue;
                }

                if (0 > add_member_2namelist(nodeName, basenameList)) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                "calloc", numNode);
                    gf_list_free(nodeNames, gf_free_proxy_string);
                    quickpool_errno = ERROR_MEMORY;
                    return -1;
                }
            }

            strIgnoreMember = groupMember->names[i];
            FREEUP(strIgnoreMember);
            groupMember->names[i] = groupMember->names[groupMember->num_name-1];
            groupMember->names[groupMember->num_name-1] = NULL;
            groupMember->num_name --;
            i --;

            FREEUP(strSubGroup);
            gf_list_free(nodeNames, gf_free_proxy_string);
            continue;
        }
        
        if (!firstNode) {
            struct hostent *hent;
            hent = gf_gethostbyname(strSubGroup);
            if (NULL == hent) {
                log_message(ERR, NOCLASS, "%s: In %s%s, exclude a bad node/node group member <%s> from group <%s>. The member is ignored.",
                            __func__, fname, section, strMember, groupinfo->group_name);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;            
                FREEUP(strSubGroup);
                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }

            if (!is_server(hent->h_name)) {

                log_message(ERR, NOCLASS, "%s: In %s%s, exclude a member <%s> who is not a server in the cluster from group <%s>. The member is ignored.",
                            __func__, fname, section, hent->h_name, groupinfo->group_name);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;            
                FREEUP(strSubGroup);
                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }
        }

        FREEUP(strSubGroup);
    }

    if (i < groupMember->num_name) {
        if (0 != i) {
            char *strMemberHead;
            strMemberHead = groupMember->names[0];
            groupMember->names[0] = groupMember->names[i];
            groupMember->names[i] = strMemberHead;
            FREEUP(groupinfo->exclude_members);
            groupinfo->exclude_members = NULL;
        }

        for (i=1; i<groupMember->num_name; i++) {

            char *strMember;
            strMember = groupMember->names[i];

            if ('~' != strMember[0]) {
                log_message(ERR, NOCLASS, "%s: In %s%s, group <%s> member has 'all'. The member <%s> is ignored.",
                            __func__, fname, section, groupinfo->group_name, strMember);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;

                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }

            strMember ++;
            if (0 == strlen(strMember)) {
                log_message(ERR, NOCLASS, "%s: In %s%s, nothing specified after '~' for group <%s>.",
                            __func__, fname, section, groupinfo->group_name);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;            
                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }     

            struct hostent *hent;
            hent = gf_gethostbyname(strMember);
            if (NULL == hent) {
                log_message(ERR, NOCLASS, "%s: In %s%s, exclude a bad node/node group member <%s> in group <%s>. The member is ignored.",
                            __func__, fname, section, strMember, groupinfo->group_name);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;            
                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }            

            if (!is_server(hent->h_name)) {

                log_message(ERR, NOCLASS, "%s: In %s%s, exclude a member <%s> who is not a server in the cluster. The member is ignored.",
                            __func__, fname, section, hent->h_name);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;            
                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }

            if (NULL == groupinfo->exclude_members) {
                groupinfo->exclude_members = copy_string(strMember);
            } else {

                char *strNewMembers;
                unsigned int length;
                length = strlen(groupinfo->exclude_members) + strlen(strMember) + 2;
                strNewMembers = (char *)realloc(groupinfo->exclude_members, length+1);
                if (NULL == strNewMembers) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "realloc", length);
                    quickpool_errno = ERROR_MEMORY;
                    return -1;
                }

                unsigned int lenExMember;
                lenExMember = strlen(groupinfo->exclude_members);
                groupinfo->exclude_members = strNewMembers;
                snprintf(groupinfo->exclude_members+lenExMember, length, " %s", strMember);
            }

            strIgnoreMember = groupMember->names[i];
            FREEUP(strIgnoreMember);
            groupMember->names[i] = groupMember->names[groupMember->num_name-1];
            groupMember->names[groupMember->num_name-1] = NULL;
            groupMember->num_name --;
            i --;            
        }
    }

    if (0 == groupMember->num_name && 0 == basenameList->num_name) {
        log_message(ERR, NOCLASS, "%s: In %s%s, no valid member defined for group <%s>. The group is ignored.",
                    __func__, fname, section, groupinfo->group_name);

        free_namelist(groupMember);
        quickpool_errno = ERROR_CONF_WARNING;

        return -1;
    }

    groupinfo->member_list = merge_namelist(groupMember, basenameList);
    if (NULL == groupinfo->member_list) {

        log_message(ERR, NOCLASS, "%s: not enough memory to parse group <%s>. The group is ignored.",
                    __func__, groupinfo->group_name);
        free_namelist(basenameList);
        free_namelist(groupMember);
        quickpool_errno = ERROR_CONF_WARNING;

        return -1;
    }

    free_namelist(basenameList);
    return 0;
} // end function parse_nodegroup_member

/**
 * @brief       Check host group parameters.
 *
 * @param[in]   param         #1: host group parameters
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parameter has syntax error
 */
static bool_t
check_host_group(struct config_param *param, const char *fname)
{
    if (NULL == param[HOSTGROUP_NAME_INDEX].string_value) {
        log_message(ERR, NOCLASS, "%s: In %s HostGroup section, GROUP_NAME must be specified.",
                    __func__, fname);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    if (strcmp(param[HOSTGROUP_NAME_INDEX].string_value, ALL_STRING) == 0
        || strcmp(param[HOSTGROUP_NAME_INDEX].string_value, DEFAULT_STRING) == 0
        || strcmp(param[HOSTGROUP_NAME_INDEX].string_value, "others") == 0) {

        log_message(WARNING, NOCLASS, "%s: In %s HostGroup section, group name <%s> conflicts with reserved word <all/default/others>",
                    __func__, fname, param[HOSTGROUP_NAME_INDEX].string_value);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    char *groupName = param[HOSTGROUP_NAME_INDEX].string_value;
    if (get_hostgroup(groupName)) {
        log_message(WARNING, NOCLASS, "%s: In %s HostGroup section, group name <%s> is multiply defined.",
                    __func__, fname, groupName);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    if (0 > param[HOSTGROUP_MEMBER_INDEX].index) {
        log_message(WARNING, NOCLASS, "%s: In %s HostGroup section, no member specified for <%s>, ignored.",
                    __func__, fname, groupName);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    if (sptrHostConf->num_host_groups >= MAX_GROUPS) {
        log_message(ERR, NOCLASS, "%s: In %s HostGroup section, the number of configured host groups reaches the limit <%d>",
                    __func__, fname,  MAX_GROUPS);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    return TRUE;
} // end function check_host_group

/**
 * @brief       Add host group to sptrHostConf.
 *
 * @param[in]   param         #1: host group parameters
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parameter has syntax error
 */
static bool_t
add_conf_nodegroup(struct config_param *param, const char *fname)
{
    /* run group for ! member list */
    if (strcmp(param[HOSTGROUP_MEMBER_INDEX].string_value, "!") == 0) {
        char *members;

        if ((members = run_group("-m", param[HOSTGROUP_NAME_INDEX].string_value)) != NULL) {
            FREEUP(param[HOSTGROUP_MEMBER_INDEX].string_value);
            param[HOSTGROUP_MEMBER_INDEX].string_value = members;
        } else {
            param[HOSTGROUP_MEMBER_INDEX].string_value[0] = '\0';
        }
    }

    struct QuickPoolHostGroupInfo *hostGroup=NULL;
    if (0 == sptrHostConf->num_host_groups) {
        sptrHostConf->host_groups = (struct QuickPoolHostGroupInfo *)calloc(1, sizeof(struct QuickPoolHostGroupInfo));
        if (NULL == sptrHostConf->host_groups) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sizeof(struct QuickPoolHostGroupInfo));
            quickpool_errno = ERROR_MEMORY;

            return FALSE;
        }

        hostGroup = &(sptrHostConf->host_groups[0]);
    } else {
        sptrHostConf->host_groups = (struct QuickPoolHostGroupInfo *)realloc(sptrHostConf->host_groups, (sptrHostConf->num_host_groups+1)*sizeof(struct QuickPoolHostGroupInfo));
        if (NULL == sptrHostConf->host_groups) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "realloc",
                        (sptrHostConf->num_host_groups+1)*sizeof(struct QuickPoolHostGroupInfo));
            quickpool_errno = ERROR_MEMORY;

            return FALSE;
        }

        hostGroup = &(sptrHostConf->host_groups[sptrHostConf->num_host_groups]);
        memset(hostGroup, 0, sizeof(struct QuickPoolHostGroupInfo));
    }

    sptrHostConf->num_host_groups ++;
    hostGroup->group_name = copy_string(param[HOSTGROUP_NAME_INDEX].string_value);
    hostGroup->max_slots = param[HOSTGROUP_MAXSLOT_INDEX].int_value;

    if (0 > param[HOSTGROUP_MEMBER_INDEX].index) {
        return TRUE;
    }

    int ret;
    ret = parse_nodegroup_member(param[HOSTGROUP_MEMBER_INDEX].string_value, hostGroup,
                                 fname, " GROUP_MEMBER section");
    if (0 > ret && quickpool_errno == ERROR_MEMORY) {
        return FALSE;
    }

    if (NULL == hostGroup->member_list) {
        log_message(WARNING, NOCLASS, "%s: In %s HostGroup section, no valid member in host group <%s>.",
                    __func__, fname, hostGroup->group_name);
        quickpool_errno = ERROR_CONF_WARNING;
        FREEUP(hostGroup->group_name);

        sptrHostConf->num_host_groups --;
        if (0 == sptrHostConf->num_host_groups) {
            FREEUP(sptrHostConf->host_groups);
        }
    }

    return TRUE;
} // end function add_conf_nodegroup

/**
 * @brief       Parse HostGroup section in qp.hosts file.
 *
 * @param[in]   parser        #1: yaml parser hanlder
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 *
 * @note        parse, check and add
 */
static bool_t
parse_host_group(yaml_parser_t *parser, const char *fname)
{
    yaml_token_t token;

    yaml_parser_scan(parser, &token);
    if (token.type != YAML_VALUE_TOKEN) {
        yaml_token_delete(&token);

        log_message(ERR, NOCLASS, "%s: there is format error in HostGroup section of file(%s) at line %d.",
                    __func__, fname, parser->mark.line);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }
    yaml_token_delete(&token);

    struct config_param groupParam[] = {
        {"GROUP_NAME", STRING_PARAM, -1, {NULL}},
        {"GROUP_MEMBER", STRING_PARAM, -1, {NULL}},
        {"MAX_SLOTS", INT_PARAM, -1, {0}},
        {NULL, STRING_PARAM, -1, {NULL}},
    };

    groupParam[HOSTGROUP_MAXSLOT_INDEX].int_value = INFINIT_INT;

    char *strKey=NULL;
    unsigned int numMapping=0, numHostGroup=0;
    int index=-1;
    do {
        yaml_parser_scan(parser, &token);

        if (YAML_NO_ERROR != parser->error) {

            log_message(ERR, NOCLASS, "%s: error occurred when parsing HostGroup section of file(%s) at line %d: %s",
                        __func__, fname, parser->mark.line, parser->problem);

            free_param_value(groupParam);
            quickpool_errno = ERROR_CONF_WARNING;

            return FALSE;
        }

        switch(token.type) {
        case YAML_FLOW_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_MAPPING_END_TOKEN:
            numMapping --;
            break;
        case YAML_FLOW_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_SEQUENCE_END_TOKEN:
            numMapping --;
            break;
        case YAML_BLOCK_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_END_TOKEN:
            numMapping --;
            break;
        case YAML_SCALAR_TOKEN:
            if (NULL == strKey) {
                index = match_param_key((char *)token.data.scalar.value, groupParam);
                if (index < 0) {
                    log_message(WARNING, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                                __func__, fname, parser->mark.line, (char *)token.data.scalar.value);
                    quickpool_errno = ERROR_CONF_WARNING;
                    break;
                }

                strKey = copy_string((char *)token.data.scalar.value);
            } else {

                if (0 == strcasecmp(strKey, groupParam[HOSTGROUP_NAME_INDEX].param_name)) {
                    numHostGroup ++;

                    if (numHostGroup > 1) {

                        if (check_host_group(groupParam, fname)) {
                            add_conf_nodegroup(groupParam, fname);
                        }

                        reset_param_value(groupParam);
                        groupParam[HOSTGROUP_MAXSLOT_INDEX].int_value = INFINIT_INT;
                    }
                }

                if (!set_param_value(index, (char *)token.data.scalar.value, groupParam)) {
                    log_message(ERR, NOCLASS, "%s: error occurred when parsing HostGroup section of file(%s) at line %d: see above for reason",
                                __func__, fname, parser->mark.line);
                    quickpool_errno = ERROR_CONF_WARNING;
                }

                index = -1;
                FREEUP(strKey);
            }

            break;
        default:
            break;
        }

        if ((token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN) || numMapping > 0) {
            yaml_token_delete(&token);
        }
    } while (token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN);
    yaml_token_delete(&token);

    if (numHostGroup > 0 && check_host_group(groupParam, fname)) {
        add_conf_nodegroup(groupParam, fname);
    }

    free_param_value(groupParam);

    return TRUE;
} // end function parse_host_group

static void
free_nodegroup_info(struct QuickPoolHostGroupInfo *group)
{
    if (NULL == group) {
        return;
    }

    FREEUP(group->group_name);
    free_namelist(group->member_list);
    group->member_list = NULL;
    FREEUP(group->exclude_members);

    return;
}

static struct QuickPoolUserInfo *
get_userinfo(char *name)
{
    if (NULL == name) {
        return NULL;
    }

    unsigned int i;
    for (i = 0; i < sptrUserConf->num_users; i++) {
        if (strcmp(name, sptrUserConf->users[i].user) == 0) {
            return (&(sptrUserConf->users[i]));
        }
    }

    return NULL;
} // end function get_userinfo

/**
 * @brief       Initialize QuickPoolUserInfo. 
 *
 * @param[out]  uinfo      #1: user info to be initialized
 */
static void
initialize_userinfo(struct QuickPoolUserInfo *uinfo)
{
    if (NULL == uinfo) {
        return;
    }

    memset(uinfo, 0, sizeof(struct QuickPoolUserInfo));

    uinfo->max_num_slot = INFINIT_INT;
    uinfo->num_job_slot = INFINIT_INT;
    uinfo->num_pending_slot = INFINIT_INT;
    uinfo->num_running_slot = INFINIT_INT;
    uinfo->num_SSUSP_slot = INFINIT_INT;
    uinfo->num_USUSP_slot = INFINIT_INT;
    uinfo->num_reserving_slot = INFINIT_INT;

    return;
} // end function initialize_userinfo

/**
 * @brief       Check user parameters.
 *
 * @param[in]   param         #1: user parameters
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parameter has syntax error
 */
static bool_t
check_user(struct config_param *param, const char *fname)
{
#define USER_NAME_INDEX     0
#define USER_MAXJOBS_INDEX  1

    if (NULL == param[USER_NAME_INDEX].string_value) {
        log_message(ERR, NOCLASS, "%s: In %s user section, user name must be specified.",
                    __func__, fname);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    if (NULL != get_userinfo(param[USER_NAME_INDEX].string_value)) {

        log_message(ERR, NOCLASS, "%s: In %s user section, user name <%s> is multiply defined.",
                    __func__, param[USER_NAME_INDEX].string_value);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    return TRUE;
} // end function check_user

/**
 * @brief       Add user to sptrUserConf.
 *
 * @param[in]   param         #1: user parameters
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parameter has syntax error
 */
static bool_t
add_conf_user(struct config_param *param, const char *fname)
{
    unsigned int splash;
    char *sp = param[USER_NAME_INDEX].string_value;
    bool_t hasAt = FALSE;
    splash = strlen(sp) - 1;
    if (0 < splash && sp[splash] == '@') {
        hasAt = TRUE;
        sp[splash] = '\0';
    }

    char *strUserGroup = NULL;
    splash = strlen(sp) - 1;
    if (0 < splash && sp[splash] == '/') {
        strUserGroup = copy_string(sp);
    }

    struct QuickPoolUserGroupInfo *gp;
    struct passwd *pw;

    gp= get_usergroup(sp);
    pw = get_user_byname(sp);

    struct group *unixGrp = NULL;
    if (!gp && (strUserGroup || (strcmp(sp, DEFAULT_STRING) && !pw))) {
        if (strUserGroup) {

            unixGrp = get_group_byname(strUserGroup);
            strUserGroup[splash] = '/';
        } else {
            unixGrp = get_group_byname(sp);
        }

        if (NULL != unixGrp) {
            gp = add_os_grp(unixGrp, sp, fname, " user section");
            if (NULL == gp) {

                log_message(WARNING, NOCLASS, "%s: In %s user section, no valid users defined in Unix group <%s>.",
                            __func__, fname, sp);
                quickpool_errno = ERROR_CONF_WARNING;
            }
        } else {
            log_message(WARNING, NOCLASS, "%s: In %s user section, unknown user <%s> is defined.",
                        __func__, fname, sp);
            quickpool_errno = ERROR_CONF_WARNING;
        }
    }

    int isGroupAt = FALSE;
    if (hasAt && NULL != gp) {
        isGroupAt = TRUE;
    }

    if (hasAt) {
        sp[splash+1] = '@';
    }

    if (!isGroupAt) {
        gf_hash_install(shashDefinedUsers, sp, (void *)sp);

        struct QuickPoolUserInfo *user=NULL;
        if (sptrUserConf->num_users == 0) {
            sptrUserConf->users = (struct QuickPoolUserInfo *)calloc(1, sizeof(struct QuickPoolUserInfo));
            if (NULL == sptrUserConf->users) {
                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                            "calloc", sizeof(struct QuickPoolUserInfo));
                quickpool_errno = ERROR_MEMORY;

                return FALSE;
            }

            user = &(sptrUserConf->users[0]);
        } else {
            sptrUserConf->users = (struct QuickPoolUserInfo *)realloc(sptrUserConf->users, (sptrUserConf->num_users+1)*sizeof(struct QuickPoolUserInfo));
            if (NULL == sptrUserConf->users) {
                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                            "realloc", (sptrUserConf->num_users+1)*sizeof(struct QuickPoolUserInfo));
                quickpool_errno = ERROR_MEMORY;

                return FALSE;
            }

            user = &(sptrUserConf->users[sptrUserConf->num_users]);
        }

        sptrUserConf->num_users ++;

        initialize_userinfo(user);

        user->user = copy_string(sp);
        if (0 < param[USER_MAXJOBS_INDEX].index) {
            user->max_num_slot = param[USER_MAXJOBS_INDEX].int_value;
        }

        FREEUP(strUserGroup);

        return TRUE;
    }

    char **groupMembers;
    int numMembers = 0;
    char *groupName = copy_string(sp);

    if ((groupMembers = expand_group(groupName, &numMembers)) == NULL) {
        log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "expand_group");
        return FALSE;
    }

    if (strcmp(groupMembers[0], ALL_STRING) == 0) {
        log_message(ERR, NOCLASS, "%s: In %s user section, user group <%s> with no members is ignored",
                    __func__, fname, sp);
    } else {
        int i;
        for (i = 0; i < numMembers; i++) {
            if (NULL != gf_hash_find(shashDefinedUsers, groupMembers[i])) {
                continue;
            }

            struct QuickPoolUserInfo *user=NULL;
            if (sptrUserConf->num_users == 0) {
                sptrUserConf->users = (struct QuickPoolUserInfo *)calloc(1, sizeof(struct QuickPoolUserInfo));
                if (NULL == sptrUserConf->users) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                "calloc", sizeof(struct QuickPoolUserInfo));
                    quickpool_errno = ERROR_MEMORY;

                    return FALSE;
                }

                user = &(sptrUserConf->users[0]);
            } else {
                sptrUserConf->users = (struct QuickPoolUserInfo *)realloc(sptrUserConf->users, (sptrUserConf->num_users+1)*sizeof(struct QuickPoolUserInfo));
                if (NULL == sptrUserConf->users) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                "realloc", (sptrUserConf->num_users+1)*sizeof(struct QuickPoolUserInfo));
                    quickpool_errno = ERROR_MEMORY;

                    return FALSE;
                }

                user = &(sptrUserConf->users[sptrUserConf->num_users]);
            }

            sptrUserConf->num_users ++;

            initialize_userinfo(user);

            user->user = copy_string(groupMembers[i]);
            if (0 < param[USER_MAXJOBS_INDEX].index) {
                user->max_num_slot = param[USER_MAXJOBS_INDEX].int_value;
            }
        }
    }

    free_string_array(groupMembers, numMembers);
    FREEUP(strUserGroup);

    return TRUE;
} // end function add_conf_user

/**
 * @brief       Parse User section in qp.users file.
 *
 * @param[in]   parser        #1: yaml parser hanlder
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 *
 * @note        parse, check and add
 */
static bool_t
parse_user(yaml_parser_t *parser, const char *fname)
{
    yaml_token_t token;

    yaml_parser_scan(parser, &token);
    if (token.type != YAML_VALUE_TOKEN) {
        yaml_token_delete(&token);

        log_message(ERR, NOCLASS, "%s: there is format error in user section of file(%s) at line %d.",
                    __func__, fname, parser->mark.line);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }
    yaml_token_delete(&token);

    struct config_param userParam[] = {
        {"USER_NAME", STRING_PARAM, -1, {NULL}},
        {"MAX_JOBS", INT_PARAM, -1, {NULL}},
        {"JL/P", INT_PARAM, -1, {NULL}},
        {NULL, STRING_PARAM, -1, {NULL}}
    };

    char *strKey=NULL;
    unsigned int numMapping=0, numUser=0;
    int index=-1;
    do {
        yaml_parser_scan(parser, &token);

        if (YAML_NO_ERROR != parser->error) {

            log_message(ERR, NOCLASS, "%s: error occurred when parsing user section of file(%s) at line %d: %s",
                        __func__, fname, parser->mark.line, parser->problem);

            quickpool_errno = ERROR_CONF_WARNING;
            free_param_value(userParam);

            return FALSE;
        }

        switch(token.type) {
        case YAML_FLOW_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_MAPPING_END_TOKEN:
            numMapping --;
            break;
        case YAML_FLOW_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_SEQUENCE_END_TOKEN:
            numMapping --;
            break;
        case YAML_BLOCK_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_END_TOKEN:
            numMapping --;
            break;
        case YAML_SCALAR_TOKEN:
            if (NULL == strKey) {
                index = match_param_key((char *)token.data.scalar.value, userParam);
                if (index < 0) {
                    log_message(WARNING, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                                __func__, fname, parser->mark.line, (char *)token.data.scalar.value);
                    quickpool_errno = ERROR_CONF_WARNING;
                    break;
                }

                strKey = copy_string((char *)token.data.scalar.value);
            } else {

                if (0 == strcasecmp(strKey, userParam[USER_NAME_INDEX].param_name)) {
                    numUser ++;

                    if (numUser > 1) {

                        if (check_user(userParam, fname)) {
                            add_conf_user(userParam, fname);
                        }
                        reset_param_value(userParam);
                    }
                }

                if (!set_param_value(index, (char *)token.data.scalar.value, userParam)) {
                    log_message(ERR, NOCLASS, "%s: error occurred when parsing user section of file(%s) at line %d: see above for reason",
                                __func__, fname, parser->mark.line);
                    quickpool_errno = ERROR_CONF_WARNING;
                }

                index = -1;
                FREEUP(strKey);
            }

            break;
        default:
            break;
        }

        if ((token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN) || numMapping > 0) {
            yaml_token_delete(&token);
        }
    } while (token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN);
    yaml_token_delete(&token);

    if (numUser > 0 && check_user(userParam, fname)) {
        add_conf_user(userParam, fname);
    }

    free_param_value(userParam);

    return TRUE;
} // end function parse_user

/**
 * @brief       Check user group parameters.
 *
 * @param[in]   param         #1: user group parameters
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parameter has syntax error
 */
static bool_t
check_user_group(struct config_param *param, const char *fname)
{
#define USERGROUP_NAME_INDEX       0
#define USERGROUP_MEMBER_INDEX     1
#define USERGROUP_USERSHARES_INDEX 2

    if (NULL == param[USERGROUP_NAME_INDEX].string_value) {
        log_message(ERR, NOCLASS, "%s: In %s UserGroup section, GROUP_NAME must be specified.",
                    __func__, fname);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    if (strcmp(param[USERGROUP_NAME_INDEX].string_value, ALL_STRING) == 0
        || strcmp(param[USERGROUP_NAME_INDEX].string_value, DEFAULT_STRING) == 0
        || strcmp(param[USERGROUP_NAME_INDEX].string_value, "others") == 0) {

        log_message(WARNING, NOCLASS, "%s: In %s UserGroup section, group name <%s> conflicts with reserved word <all/default/others>",
                    __func__, fname, param[USERGROUP_NAME_INDEX].string_value);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    char *groupName = param[USERGROUP_NAME_INDEX].string_value;
    if (get_usergroup(groupName)) {
        log_message(WARNING, NOCLASS, "%s: In %s UserGroup section, group name <%s> is multiply defined.",
                    __func__, fname, groupName);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    if (0 <= param[USERGROUP_USERSHARES_INDEX].index) {

        char *sp;
        sp = strip_spaces(param[USERGROUP_USERSHARES_INDEX].string_value, FALSE);

        if ('[' != sp[0]) {
            log_message(ERR, NOCLASS, "%s: In %s UserGroup section, USER_SHARES <%s> not start with '['.",
                        __func__, fname, param[USERGROUP_USERSHARES_INDEX].string_value);
            quickpool_errno = ERROR_CONF_WARNING;
            param[USERGROUP_USERSHARES_INDEX].index = -1;
        } else {

            int numUserShare=0;
            while (sp != NULL && sp[0] != '\0') {

                char *strUserShare;
                strUserShare = get_string_inside(&sp, '[', ']');
                if (NULL == strUserShare || 0 == strlen(strUserShare)) {
                    log_message(ERR, NOCLASS, "%s: In %s UserGroup section, %s has nothing defined insied [ ].",
                                __func__, fname, param[USERGROUP_USERSHARES_INDEX].string_value);
                    quickpool_errno = ERROR_CONF_WARNING;
                    param[USERGROUP_USERSHARES_INDEX].index = -1;

                    numUserShare = -1;
                    FREEUP(strUserShare);
                    break;
                }

                char *strValue;
                strValue = strchr(strUserShare, ',');
                if (NULL == strValue) {
                    log_message(ERR, NOCLASS, "%s: In %s UserGroup section, USER_SHARES <%s> does not have ','.",
                                __func__, fname, strUserShare);
                    quickpool_errno = ERROR_CONF_WARNING;

                    numUserShare = -1;
                    FREEUP(strUserShare);
                    break;
                }

                strValue ++;
                if (INFINIT_INT == gf_atoi(strValue, INFINIT_INT, 0)) {
                    log_message(ERR, NOCLASS, "%s: In %s UserGroup section, USER_SHARES <%s> value [%s] is not an integer between 1 and %d.",
                                __func__, fname, strUserShare, strValue, INFINIT_INT);
                    quickpool_errno = ERROR_CONF_WARNING;

                    numUserShare = -1;
                    FREEUP(strUserShare);
                    break;
                }

                numUserShare ++;
            }

            if (0 == numUserShare) {
                log_message(ERR, NOCLASS, "%s: In %s UserGroup section, no valid user shares found in <%s>.",
                            __func__, fname, param[USERGROUP_USERSHARES_INDEX].string_value);
                quickpool_errno = ERROR_CONF_WARNING;
                param[USERGROUP_USERSHARES_INDEX].index = -1;
            }
        }
    }

    if (sptrUserConf->num_user_groups >= MAX_GROUPS) {
        log_message(ERR, NOCLASS, "%s: In %s UserGroup section, the number of configured user groups reaches the limit <%d>, user group <%s> is not added.",
                    __func__, fname,  MAX_GROUPS, groupName);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    return TRUE;
} // end function check_user_group

static int
parse_usergroup_member(char *members, struct QuickPoolUserGroupInfo *groupinfo,
                       const char *fname, const char *section)
{
    if (NULL == groupinfo || NULL == members) {
        log_message(ERR, NOCLASS, "%s: input members or groupinfo is NULL.", __func__);
        quickpool_errno = ERROR_ARGUMENT;
        return FALSE;
    }

    struct nameList *groupMember;
    groupMember = longstring_2namelist(members);
    if (NULL == groupMember) {
        log_message(ERR, NOCLASS, STR_FUNC_S_FAIL, __func__,
                    "longstring_2namelist", "calloc");
        quickpool_errno = ERROR_MEMORY;
        return -1;
    }

    char *strIgnoreMember;

    unsigned int i;
    for (i=0; i<groupMember->num_name; i++) {

        char *strMember;
        strMember = groupMember->names[i];

        if ('~' == strMember[0]) {

            strMember ++;
            if (0 == strlen(strMember)) {
                log_message(ERR, NOCLASS, "%s: In %s%s, nothing specified after '~' for group <%s>.",
                            __func__, fname, section, groupinfo->group_name);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;            
                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }

            struct passwd *pw = NULL;
            pw = get_user_byname(strMember);
            if (NULL == pw) {

                log_message(ERR, NOCLASS, "%s: In %s%s, exclude a member <%s> who is not valid user from group <%s>. The member is ignored.",
                            __func__, fname, section, strMember, groupinfo->group_name);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;            
                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }

            if (NULL == groupinfo->exclude_members) {
                groupinfo->exclude_members = copy_string(strMember);
            } else {

                char *strNewMembers;
                unsigned int length;
                length = strlen(groupinfo->exclude_members) + strlen(strMember) + 2;
                strNewMembers = (char *)realloc(groupinfo->exclude_members, length);
                if (NULL == strNewMembers) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "realloc", length);
                    free_namelist(groupMember);
                    quickpool_errno = ERROR_MEMORY;
                    return -1;
                }

                unsigned int lenExMember;
                lenExMember = strlen(groupinfo->exclude_members);
                groupinfo->exclude_members = strNewMembers;
                snprintf(groupinfo->exclude_members+lenExMember, length, " %s", strMember);
            }

            strIgnoreMember = groupMember->names[i];
            FREEUP(strIgnoreMember);
            groupMember->names[i] = groupMember->names[groupMember->num_name-1];
            groupMember->names[groupMember->num_name-1] = NULL;
            groupMember->num_name --;
            i --;            

            continue;
        }

        if (0 == strcmp(groupMember->names[i], ALL_STRING)) {
            break;
        }

        char *strSubGroup=NULL;

        int splash;
        splash = strlen(groupMember->names[i]) - 1;
        if (0 < splash && '/' == groupMember->names[i][splash]) {

            strSubGroup = copy_string(groupMember->names[i]);
            strSubGroup[splash] = '\0';
        } else {
            strSubGroup = copy_string(groupMember->names[i]);
            splash = 0;
        }

        struct QuickPoolUserGroupInfo *subUserGroup;
        subUserGroup = get_usergroup(strSubGroup);

        if (groupinfo == subUserGroup) {

            log_message(ERR, NOCLASS, "%s: In %s%s, recursive member defined in group <%s>. The member is ignored.",
                        __func__, fname, section, groupinfo->group_name);

            strIgnoreMember = groupMember->names[i];
            FREEUP(strIgnoreMember);
            groupMember->names[i] = groupMember->names[groupMember->num_name-1];
            groupMember->names[groupMember->num_name-1] = NULL;
            groupMember->num_name --;
            i --;

            FREEUP(strSubGroup);
            quickpool_errno = ERROR_CONF_WARNING;

            continue;
        } else if (NULL != subUserGroup) { // cluster user group

            FREEUP(strSubGroup);
            if (0 == splash) {
                unsigned int len;
                len = strlen(groupMember->names[i])+2;
                strSubGroup = (char *)calloc(len+1, sizeof(char));
                if (NULL == strSubGroup) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                "calloc", strlen(groupMember->names[i])+2);
                    free_namelist(groupMember);
                    quickpool_errno = ERROR_MEMORY;
                    return -1;
                }

                snprintf(strSubGroup, len, "%s/", groupMember->names[i]);
                FREEUP(groupMember->names[i]);
                groupMember->names[i] = strSubGroup;
            }

            continue;
        }

        if (NULL == shashUnknownUsers) {
            shashUnknownUsers = gf_hash_make(101);
        } else {

            if (NULL != gf_hash_find(shashUnknownUsers, strSubGroup)) {

                log_message(ERR, NOCLASS, "%s: In %s%s, unknown group member <%s>.",
                            __func__, fname, section, strSubGroup);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;

                FREEUP(strSubGroup);
                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }
        }

        struct passwd *pw = NULL;
        if (0 == splash) {
            pw = get_user_byname(strSubGroup);
        }

        if (NULL != pw) { // valid group member
            FREEUP(strSubGroup);
            continue;
        }

        struct group *osGroup = NULL;
        osGroup = get_group_byname(strSubGroup);
        if (NULL != osGroup) {

            subUserGroup = add_os_grp(osGroup, strSubGroup, fname, section);
            if (NULL == subUserGroup) {

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;

                FREEUP(strSubGroup);

                if (quickpool_errno == ERROR_MEMORY) {
                    free_namelist(groupMember);
                    return -1;
                }

                continue;
            }

            FREEUP(strSubGroup);
            if (0 == splash) {
                unsigned int len;
                len = strlen(groupMember->names[i])+2;
                strSubGroup = (char *)calloc(len+1, sizeof(char));
                if (NULL == strSubGroup) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                "calloc", strlen(groupMember->names[i])+2);
                    quickpool_errno = ERROR_MEMORY;
                    return -1;
                }

                snprintf(strSubGroup, len, "%s/", groupMember->names[i]);
                FREEUP(groupMember->names[i]);
                groupMember->names[i] = strSubGroup;
            }
        } else {

            log_message(WARNING, NOCLASS, "%s: In %s%s, unknown group member <%s> in group <%s>.",
                        __func__, fname, section, strSubGroup, groupinfo->group_name);

            quickpool_errno = ERROR_CONF_WARNING;
            gf_hash_install(shashUnknownUsers, strSubGroup, strSubGroup);

            strIgnoreMember = groupMember->names[i];
            FREEUP(strIgnoreMember);
            groupMember->names[i] = groupMember->names[groupMember->num_name-1];
            groupMember->names[groupMember->num_name-1] = NULL;
            groupMember->num_name --;
            i --;

            FREEUP(strSubGroup);

            continue;
        }
    }

    if (i < groupMember->num_name) {
        if (0 != i) {
            char *strMemberHead;
            strMemberHead = groupMember->names[0];
            groupMember->names[0] = groupMember->names[i];
            groupMember->names[i] = strMemberHead;
        }

        for (i=1; i<groupMember->num_name; i++) {

            char *strMember;
            strMember = groupMember->names[i];

            if ('~' != strMember[0]) {
                log_message(ERR, NOCLASS, "%s: In %s%s, group <%s> member has 'all'. The member <%s> is ignored.",
                            __func__, fname, section, groupinfo->group_name, strMember);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;

                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }

            strMember ++;
            if (0 == strlen(strMember)) {
                log_message(ERR, NOCLASS, "%s: In %s%s, nothing specified after '~' for group <%s>.",
                            __func__, fname, section, groupinfo->group_name);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;            
                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }

            struct passwd *pw = NULL;
            pw = get_user_byname(strMember);
            if (NULL == pw) {

                log_message(ERR, NOCLASS, "%s: In %s%s, exclude a member <%s> who is not valid user from group <%s>. The member is ignored.",
                            __func__, fname, section, strMember, groupinfo->group_name);

                strIgnoreMember = groupMember->names[i];
                FREEUP(strIgnoreMember);
                groupMember->names[i] = groupMember->names[groupMember->num_name-1];
                groupMember->names[groupMember->num_name-1] = NULL;
                groupMember->num_name --;
                i --;            
                quickpool_errno = ERROR_CONF_WARNING;

                continue;
            }

            if (NULL == groupinfo->exclude_members) {
                groupinfo->exclude_members = copy_string(strMember);
            } else {

                char *strNewMembers;
                unsigned int length;
                length = strlen(groupinfo->exclude_members) + strlen(strMember) + 2;
                strNewMembers = (char *)realloc(groupinfo->exclude_members, length);
                if (NULL == strNewMembers) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "realloc", length);
                    quickpool_errno = ERROR_MEMORY;
                    return -1;
                }

                unsigned int lenExMember;
                lenExMember = strlen(groupinfo->exclude_members);
                groupinfo->exclude_members = strNewMembers;
                snprintf(groupinfo->exclude_members+lenExMember, length, " %s", strMember);
            }

            strIgnoreMember = groupMember->names[i];
            FREEUP(strIgnoreMember);
            groupMember->names[i] = groupMember->names[groupMember->num_name-1];
            groupMember->names[groupMember->num_name-1] = NULL;
            groupMember->num_name --;
            i --;            
        }
    }

    if (0 == groupMember->num_name) {
        log_message(ERR, NOCLASS, "%s: In %s%s, no valid member defined for group <%s>. The group is ignored.",
                    __func__, fname, section, groupinfo->group_name);

        free_namelist(groupMember);

        return -1;
    }

    groupinfo->member_list = groupMember;

    return 0 ;
} // end function parse_usergroup_member

/**
 * @brief       Add user group to sptrUserConf.
 *
 * @param[in]   param         #1: user group parameters
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parameter has syntax error
 */
static bool_t
add_conf_usergroup(struct config_param *param, const char *fname)
{
    /* run group for ! member list */
    if (strcmp(param[USERGROUP_MEMBER_INDEX].string_value, "!") == 0) {
        char *members;

        if ((members = run_group("-u", param[USERGROUP_NAME_INDEX].string_value)) != NULL) {
            FREEUP(param[USERGROUP_MEMBER_INDEX].string_value);
            param[USERGROUP_MEMBER_INDEX].string_value = members;
        } else {
            param[USERGROUP_MEMBER_INDEX].string_value[0] = '\0';
        }
    }

    struct QuickPoolUserGroupInfo *userGroup=NULL;
    if (sptrUserConf->num_user_groups == 0) {
        sptrUserConf->user_groups = (struct QuickPoolUserGroupInfo *)calloc(1, sizeof(struct QuickPoolUserGroupInfo));
        if (NULL == sptrUserConf->user_groups) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sizeof(struct QuickPoolUserGroupInfo));
            quickpool_errno = ERROR_MEMORY;

            return FALSE;
        }

        userGroup = &(sptrUserConf->user_groups[0]);
    } else {
        sptrUserConf->user_groups = (struct QuickPoolUserGroupInfo *)realloc(sptrUserConf->user_groups, (sptrUserConf->num_user_groups+1)*sizeof(struct QuickPoolUserGroupInfo));
        if (NULL == sptrUserConf->user_groups) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "realloc",
                        (sptrUserConf->num_user_groups+1)*sizeof(struct QuickPoolUserGroupInfo));
            quickpool_errno = ERROR_MEMORY;

            return FALSE;
        }

        userGroup = &(sptrUserConf->user_groups[sptrUserConf->num_user_groups]);
        memset(userGroup, 0, sizeof(struct QuickPoolUserGroupInfo));
    }

    sptrUserConf->num_user_groups ++;
    userGroup->group_name = copy_string(param[USERGROUP_NAME_INDEX].string_value);

    if (0 <= param[USERGROUP_USERSHARES_INDEX].index) {
        userGroup->user_shares = copy_string(param[USERGROUP_USERSHARES_INDEX].string_value);
    }

    if (0 > param[USERGROUP_MEMBER_INDEX].index) {
        return TRUE;
    }

    int ret;
    ret = parse_usergroup_member(param[USERGROUP_MEMBER_INDEX].string_value, userGroup,
                                 fname, " GROUP_MEMBER section");
    if (0 > ret && quickpool_errno == ERROR_MEMORY) {
        return FALSE;
    }

    return TRUE;
} // end function add_conf_usergroup

/**
 * @brief       Parse UserGroup section in qp.users file.
 *
 * @param[in]   parser        #1: yaml parser hanlder
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 *
 * @note        parse, check and add
 */
static bool_t
parse_user_group(yaml_parser_t *parser, const char *fname)
{
    yaml_token_t token;

    yaml_parser_scan(parser, &token);
    if (token.type != YAML_VALUE_TOKEN) {
        yaml_token_delete(&token);

        log_message(ERR, NOCLASS, "%s: there is format error in UserGroup section of file(%s) at line %d.",
                    __func__, fname, parser->mark.line);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }
    yaml_token_delete(&token);

    struct config_param groupParam[] = {
        {"GROUP_NAME", STRING_PARAM, -1, {NULL}},
        {"GROUP_MEMBER", STRING_PARAM, -1, {NULL}},
        {"USER_SHARES", STRING_PARAM, -1, {NULL}},
        {NULL, STRING_PARAM, -1, {NULL}}
    };

    char *strKey=NULL;
    unsigned int numMapping=0, numUserGroup=0;
    int index=-1;
    do {
        yaml_parser_scan(parser, &token);

        if (YAML_NO_ERROR != parser->error) {

            log_message(ERR, NOCLASS, "%s: error occurred when parsing UserGroup section of file(%s) at line %d: %s",
                        __func__, fname, parser->mark.line, parser->problem);

            free_param_value(groupParam);
            quickpool_errno = ERROR_CONF_WARNING;

            return FALSE;
        }

        switch(token.type) {
        case YAML_FLOW_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_MAPPING_END_TOKEN:
            numMapping --;
            break;
        case YAML_FLOW_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_SEQUENCE_END_TOKEN:
            numMapping --;
            break;
        case YAML_BLOCK_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_END_TOKEN:
            numMapping --;
            break;
        case YAML_SCALAR_TOKEN:
            if (NULL == strKey) {
                index = match_param_key((char *)token.data.scalar.value, groupParam);
                if (index < 0) {
                    log_message(WARNING, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                                __func__, fname, parser->mark.line, (char *)token.data.scalar.value);
                    quickpool_errno = ERROR_CONF_WARNING;
                    break;
                }

                strKey = copy_string((char *)token.data.scalar.value);
            } else {

                if (0 == strcasecmp(strKey, groupParam[HOSTGROUP_NAME_INDEX].param_name)) {
                    numUserGroup ++;

                    if (numUserGroup > 1) {

                        if (check_user_group(groupParam, fname)) {
                            add_conf_usergroup(groupParam, fname);
                        }

                        reset_param_value(groupParam);
                    }
                }

                if (!set_param_value(index, (char *)token.data.scalar.value, groupParam)) {
                    log_message(ERR, NOCLASS, "%s: error occurred when parsing UserGroup section of file(%s) at line %d: see above for reason",
                                __func__, fname, parser->mark.line);
                    quickpool_errno = ERROR_CONF_WARNING;
                }

                index = -1;
                FREEUP(strKey);
            }

            break;
        default:
            break;
        }

        if ((token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN) || numMapping > 0) {
            yaml_token_delete(&token);
        }
    } while (token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN);
    yaml_token_delete(&token);

    if (numUserGroup > 0 && check_user_group(groupParam, fname)) {
        add_conf_usergroup(groupParam, fname);
    }

    free_param_value(groupParam);

    return 0;
} // end function parse_user_group

static struct QuickPoolQueueInfo *
get_queueinfo(char *name)
{
    if (NULL == name) {
        return NULL;
    }

    unsigned int i;
    for (i = 0; i < sptrQueueConf->num_queues; i++) {
        if (strcmp(name, sptrQueueConf->queues[i].queue) == 0) {
            return (&(sptrQueueConf->queues[i]));
        }
    }

    return NULL;
} // end function get_queueinfo

/**
 * @brief       Initialize QuickPoolQueueInfo. 
 *
 * @param[out]  qinfo      #1: queue info to be initialized
 */
static void
initialize_queueinfo(struct QuickPoolQueueInfo *qinfo)
{
    int i;

    memset(qinfo, 0, sizeof(struct QuickPoolQueueInfo));

    qinfo->priority = INFINIT_INT;
    qinfo->nice = INFINIT_SHORT;
    qinfo->max_num_slot = INFINIT_INT;
    qinfo->mig = INFINIT_INT;
    qinfo->job_schedule_delay = INFINIT_INT;
    qinfo->job_dispatch_interval = INFINIT_INT;
    qinfo->user_slot_limit = INFINIT_INT;
    qinfo->host_slot_limit = INFINIT_INT;
    qinfo->job_slot_limit = INFINIT_INT;
    qinfo->min_job_slot = INFINIT_INT;
    qinfo->default_job_slot = INFINIT_INT;

    for (i = 0; i < RESOURCE_LIMIT_NUM; i++){
        qinfo->queue_ulimit[i] = INFINIT_INT;
        qinfo->default_ulimit[i] = INFINIT_INT;
    }

    qinfo->reservation_time = INFINIT_INT;

    return;
} // end function initialize_queueinfo

/**
 * @brief       Parse OWNER_SHIP parameter of the queue. 
 *
 * @param[out]  val        #1: onwership parameter string
 * @param[out]  queue      #2: ownership of the queue is set
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parameter has syntax error
 *
 * @note         USERS[[G1, 20] [G2, 10]] LOAN_DURATION[31]
 */
static bool_t
parse_ownership(const char *val, struct QuickPoolQueueInfo *queue)
{
    char *l;
    char *p;

    p = copy_string(val);

    /* first check for loan_duration
     */
    l = strstr(p, "LOAN_DURATION");
    if (l) {
	char *b;
	char *b2;

	*l = 0;
	++l;
	b = strchr(l,'[');
	if (b == NULL) {
	    return FALSE;
        }

	b2 = strchr(l, ']');
	if (b2 == NULL) {
	    return FALSE;
        }

	*b = 0;
	++b;
	*b2 = 0;
	queue->loan_duration = atoi(b);
    }

    queue->ownership = p;

    return TRUE;
} // end function parse_ownership

static void
free_usergroup_info(struct QuickPoolUserGroupInfo *group)
{
    if (NULL == group) {
        return;
    }

    FREEUP(group->group_name);
    free_namelist(group->member_list);
    group->member_list = NULL;
    FREEUP(group->user_shares);
    FREEUP(group);

    return;
} // end function free_usergroup_info

static char *
parse_reque_evalues(char *word, struct QuickPoolQueueInfo *qinfo, char *fname)
{
#define NORMAL_EXIT 0
#define EXCLUDE_EXIT 1

    if (NULL == word || 0 == strlen(word)) {
        log_message(ERR, NOCLASS, "%s: In %s queue section, nothing is defined in REQUEUE_EXIT_VALUES of the queue <%s>.",
                    __func__, fname, qinfo->queue);
        quickpool_errno = ERROR_CONF_WARNING;
        return NULL;
    }

    char *strReqCode;
    strReqCode = copy_string(word);

    unsigned int numExitValue=0;

    char *sp;
    sp = strReqCode;
    unsigned int length = strlen(sp);
    char *strExitValue = (char *)calloc(length+1, sizeof(char));
    if (NULL == strExitValue) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", length+1);
        quickpool_errno = ERROR_MEMORY;

        return NULL;
    }
    char *token;
    while ((token=get_string_token(&sp, " ()", strExitValue, length+1)) != NULL) {
        length = strlen(sp);

        if (0 == strcasecmp(token, "EXCLUDE")) {
            continue;
        }

        if (!isinteger(token)) {
            log_message(ERR, NOCLASS, "%s: In %s queue section, requeue exit value <%s> for queue <%s> is not an interger.",
                 __func__, fname, token, qinfo->queue);
            FREEUP(strReqCode);
            FREEUP(strExitValue);
            quickpool_errno = ERROR_CONF_WARNING;
            return NULL;
        }

        int exitV;
        exitV = gf_atoi(token, 256, -1);
        if (INFINIT_INT == exitV) {
            log_message(ERR, NOCLASS, "%s: In %s queue section, requeue exit value <%s> for queue <%s> is not an interger between 0-255.",
                        __func__, fname, token, qinfo->queue);
            FREEUP(strReqCode);
            FREEUP(strExitValue);
            quickpool_errno = ERROR_CONF_WARNING;
            return NULL;
        }

        numExitValue ++;
    }

    FREEUP(strExitValue);

    if (0 == numExitValue) {
        log_message(ERR, NOCLASS, "%s: In %s queue section, no valid requeue exit values <%s> for queue <%s>.",
                    __func__, fname, strReqCode, qinfo->queue);
        FREEUP(strReqCode);
        quickpool_errno = ERROR_CONF_WARNING;
        return NULL;
    }

    return strReqCode;
} // end function parse_reque_evalues

static void
parse_queue_limit(const char *key, char *value, unsigned int limitidx,
                  struct QuickPoolQueueInfo *qinfo, char *fname)
{
    char *defaultLimit = NULL;
    char *maxLimit = NULL;

    char *sp = value;
    char *token;
    unsigned int len = strlen(sp);

    defaultLimit = (char *)calloc(len+1, sizeof(char));
    if (NULL == defaultLimit) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
        quickpool_errno = ERROR_MEMORY;

        return;
    }
    token = get_string_token(&sp, " ", defaultLimit, len+1);
    if (token == NULL) {

        log_message(ERR, NOCLASS, "%s: In %s queue section, no valid value of %s is defined for queue <%s>.",
                    __func__, fname, key, qinfo->queue);
        FREEUP(defaultLimit);

        return;
    }

    len = strlen(sp);
    if (len > 0) {

        maxLimit = (char *)calloc(len+1, sizeof(char));
        if (NULL == maxLimit) {

            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
            quickpool_errno = ERROR_MEMORY;

            return;
        }

        token = get_string_token(&sp, " ", maxLimit, len+1);
        if (token == NULL) {
            FREEUP(maxLimit);
        }
    }

    if (NULL != maxLimit) {
        qinfo->queue_ulimit[limitidx] = atoi(maxLimit);
        qinfo->default_ulimit[limitidx] = atoi(defaultLimit);

        FREEUP(defaultLimit);
        FREEUP(maxLimit);
    } else {
        qinfo->queue_ulimit[limitidx] = atoi(defaultLimit);

        FREEUP(defaultLimit);
    }

    if (INFINIT_INT != qinfo->default_ulimit[limitidx] && INFINIT_INT != qinfo->queue_ulimit[limitidx]
        && qinfo->default_ulimit[limitidx] > qinfo->queue_ulimit[limitidx]) {

        log_message(ERR, NOCLASS, "%s: In %s queue section, the default limit <%d> of %s for queue <%s> should not be greater than the max limit <%d>.",
                    __func__, fname, qinfo->default_ulimit[limitidx], key,
                    qinfo->queue, qinfo->queue_ulimit[limitidx]);

        qinfo->default_ulimit[limitidx] = qinfo->queue_ulimit[limitidx];
    }

    return;
} // end function parse_queue_limit

static bool_t
parse_job_slot_limit(char *word, struct QuickPoolQueueInfo *qinfo, char *fname)
{
    char *sp;
    char *curWord = NULL;
    int values[3], i;

    sp = word;
    if (sp == NULL) {
        return TRUE;
    }

    while (*sp != '\0' && *sp != '#') {
        sp++;
    }

    if (*sp == '#') {
        *sp = '\0';
    }

    sp = word;

    unsigned int len = strlen(sp);
    curWord = (char *)calloc(len+1, sizeof(char));
    if (NULL == curWord) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
        quickpool_errno = ERROR_MEMORY;

        return FALSE;
    }

    char *token;
    for (i = 0; i<3; i++) {
        token = get_string_token(&sp, " ", curWord, len+1);
        if (token == NULL)
            break;
        if ((values[i] = gf_atoi(token, INFINIT_INT, 0)) == INFINIT_INT) {
            log_message(ERR, NOCLASS, "%s: In %s section Queue, PROCLIMIT value <%s> for queue %s isn't a positive integer.",
                        __func__, fname, curWord, qinfo->queue);
            FREEUP(curWord);
            return FALSE;
        }
    }

    token = get_string_token(&sp, " ", curWord, len+1);
    if (token != NULL) {
        log_message(ERR, NOCLASS, "%s: In %s section Queue, PROCLIMIT of queue %s has too many parameters. PROCLIMIT=[minimum [default]] maximum",
                    __func__, fname, qinfo->queue);
        FREEUP(curWord);
        return FALSE;
    }
    FREEUP(curWord);

    switch (i) {
    case 1:
        qinfo->job_slot_limit = values[0];
        qinfo->min_job_slot = 1;
        qinfo->default_job_slot = 1;

        break;
    case 2:
        if (values[0] > values[1]) {
            log_message(ERR, NOCLASS, "%s: In %s section Queue, PROCLIMIT values <%d %d> for queue %s are not valid. PROCLIMIT values must satisfy the following condition: 1 <= minimum <= maximum",
                        __func__, fname, values[0], values[1], qinfo->queue);
            return FALSE;
        } else {
            qinfo->min_job_slot = values[0];
            qinfo->default_job_slot = values[0];
            qinfo->job_slot_limit = values[1];
        }

        break;
    case 3:
        if (!(values[0] <= values[1] && values[1] <= values[2])) {
            log_message(ERR, NOCLASS, "%s: In %s section Queue, PROCLIMIT value <%d %d %d> for queue %s is not valid. PROCLIMIT values must satisfy the following condition: 1 <= minimum <= default <= maximum",
                        __func__, fname, values[0], values[1], values[2], qinfo->queue);
            return FALSE;
        } else {
            qinfo->min_job_slot = values[0];
            qinfo->default_job_slot = values[1];
            qinfo->job_slot_limit = values[2];
        }

        break;
    }

    return TRUE;
} // end function parse_job_slot_limit

static bool_t
parse_queue_reservation(char *reserve, struct QuickPoolQueueInfo *qinfo, char *filename)
{
    char *sp, *cp;

    if ((sp = strstr(reserve, "MAX_RESERVE_TIME")) != NULL) {
        sp += strlen("MAX_RESERVE_TIME");

        SKIPSPACE(sp)

        if (*sp == '\0') {
            log_message(ERR, NOCLASS, "%s: In %s queue section, MAX_RESERVE_TIME is specified without period time for queue %s.",
	                __func__, filename, qinfo->queue);
	    quickpool_errno = ERROR_CONF_WARNING;
            return FALSE;
        }
        if (*sp != '[') {
            log_message(ERR, NOCLASS, "%s: In %s queue section, MAX_RESERVE_TIME <%s> is specified without '[' for queue %s.",
	                __func__, filename, reserve, qinfo->queue);
	    quickpool_errno = ERROR_CONF_WARNING;
            return FALSE;
        }
        cp = ++sp;
        while (*sp != ']' && *sp != '\0' && isdigit (*sp) && *sp != ' ')
            sp++;
        if (*sp == '\0' || (*sp != ']' && *sp != ' ')) {
            log_message(ERR, NOCLASS, "%s: In %s queue section, MAX_RESERVE_TIME <%s> is specified without ']' for queue %s.",
	                __func__, filename, reserve, qinfo->queue);
	    quickpool_errno = ERROR_CONF_WARNING;
            return FALSE;
        }
        if (*sp == ' ') {
            while (*sp == ' ')
                sp++;
            if (*sp != ']') {
                log_message(ERR, NOCLASS, "%s: In %s queue section, MAX_RESERV_TIME is <%s> specified without ']' for queue %s.",
	                    __func__, filename, reserve, qinfo->queue);
	        quickpool_errno = ERROR_CONF_WARNING;
                return FALSE;
            }
        }
        *sp = '\0';
        qinfo->reservation_time = gf_atoi(cp, INFINIT_INT, 0);
        if (INFINIT_INT == qinfo->reservation_time) {
            log_message(ERR, NOCLASS, "%s: In %s queue section, value <%s> of MAX_RESERV_TIME for queue <%s> isn't an integer between 1 and %d.",
	                __func__, filename, cp, qinfo->queue, INFINIT_INT);
	    quickpool_errno = ERROR_CONF_WARNING;
            *sp = ']';
            return FALSE;
        }
        *sp = ']';
    }

    return TRUE;
} // end function parse_queue_reservation

static int
parse_queue_actcmd(char *strline, struct QuickPoolQueueInfo *qp, char *fname, const char *section)
{
    int  actClassValue = -1;

    strline = strip_spaces(strline, FALSE);

    unsigned int len = strlen(strline);
    char *actClass = (char *)calloc(len+1, sizeof(char));
    if (NULL == actClass) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
        quickpool_errno = ERROR_MEMORY;

        return -1;
    }
    char *token;
    while (NULL != (token=get_string_token(&strline, " ,[]", actClass, len+1))) {

        if (strcmp(token, "SUSPEND") == 0)
            actClassValue = 0;
        else if (strcmp(token, "RESUME") == 0)
            actClassValue = 1;
        else if (strcmp(token, "TERMINATE") == 0)
            actClassValue = 2;
        else {
            FREEUP(actClass);
            if (fname) {
                log_message(ERR, NOCLASS, "%s: In %s%s, wrong KEYWORD <%s> for queue %s.",
                            __func__, fname, section, strline, qp->queue);
            }
            quickpool_errno = ERROR_CONF_WARNING;
            return -2;
        }

        strline = strip_spaces(strline, FALSE);

        if (*strline != '[') {
            FREEUP(actClass);
            if (fname) {
                log_message(ERR, NOCLASS, "%s: In %s%s, '[' is missing in <%s> for queue %s.",
                            __func__, fname, section, strline, qp->queue);
            }
            quickpool_errno = ERROR_CONF_WARNING;
            return (-3);
        }
        strline++;

        strline = strip_spaces(strline, FALSE);

        char *sigActCmd;
        sigActCmd = strline;

        while ((strline == NULL || strline[0] == '\0') || ( *strline != ']'))
            strline++;

        if ((strline == NULL || strline[0] == '\0') || ( *strline != ']')) {
            FREEUP(actClass);
            if (fname) {
                log_message(ERR, NOCLASS, "%s: In %s%s, ']' is missing in <%s> for queue %s.",
                            __func__, fname, section, strline, qp->queue);
            }
            quickpool_errno = ERROR_CONF_WARNING;
            return (-5);
        }
        *strline++ = '\0';

        if (actClassValue == 0) {
            qp->suspend_command = copy_string(sigActCmd);
        } else if (actClassValue == 1)
            qp->resume_command = copy_string(sigActCmd);
        else if (actClassValue == 2) {
            qp->terminate_command = copy_string(sigActCmd);
        }
    }
    FREEUP(actClass);

    if (actClassValue < 0) {
        if (fname)
            log_message(ERR, NOCLASS, "%s: In %s%s, SUSPEND, RESUME or TERMINATE is missing for queue %s.",
                        __func__, fname, section, qp->queue);
        quickpool_errno = ERROR_CONF_WARNING;
        return -1;
    }

    return 0;
}

static int
parse_queue_suspend(char *strline, struct QuickPoolQueueInfo *qp, char *fname, const char *section)
{
    strline = strip_spaces(strline, FALSE);

    unsigned int len = strlen(strline);
    char *sigName = (char *)calloc(len+1, sizeof(char));
    if (NULL == sigName) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
        quickpool_errno = ERROR_MEMORY;

        return -1;
    }
    char *token;

    while ((token = get_string_token(&strline, " ", sigName, len+1)) != NULL) {
        len = strlen(strline);

        if (strcmp(token, "USER") == 0) {
            qp->quickpool_signal[-string_2qpsignal("SUSPEND_USER")] = -10;
        } else if (strcmp(token, "LOAD") == 0) {
            qp->quickpool_signal[-string_2qpsignal("SUSPEND_LOAD")] = -10;
        } else if (strcmp(token, "PREEMPT") == 0) {
            qp->quickpool_signal[-string_2qpsignal("SUSPEND_PREEMPT")] = -10;
        } else if (strcmp(token, "WINDOW") == 0) {
            qp->quickpool_signal[-string_2qpsignal("SUSPEND_WINDOW")] = -10;
        } else {

            if (fname)
                log_message(ERR, NOCLASS, "%s: In %s%s of the queue [%s], unknown keyword  <%s> for TERMINATE_WHEN.",
                            __func__, fname, section, qp->queue, token);
            quickpool_errno = ERROR_CONF_WARNING;
            return -1;
        }
    }
    FREEUP(sigName);

    return 0;
}

/**
 * @brief       Check queue parameters.
 *
 * @param[in]   param         #1: queue parameters
 * @param[in]   fname         #2: full path file name
 * @param[in]   info          #3: cluster resource table
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parameter has syntax error
 */
static bool_t
check_queue_definition(struct config_param *param, char *fname, struct quickpoolInfo *info)
{
#define QUEUE_NAME_INDEX             info->num_index
#define QUEUE_PRIORITY_INDEX         info->num_index+1
#define QUEUE_NICE_INDEX             info->num_index+2
#define QUEUE_UJOBLIMIT_INDEX        info->num_index+3
#define QUEUE_RUNWINDOW_INDEX        info->num_index+4
#define QUEUE_CPULIMIT_INDEX         info->num_index+5
#define QUEUE_FILELIMIT_INDEX        info->num_index+6
#define QUEUE_DATALIMIT_INDEX        info->num_index+7
#define QUEUE_STACKLIMIT_INDEX       info->num_index+8
#define QUEUE_CORELIMIT_INDEX        info->num_index+9
#define QUEUE_MEMLIMIT_INDEX         info->num_index+10
#define QUEUE_RUNLIMIT_INDEX         info->num_index+11
#define QUEUE_USERS_INDEX            info->num_index+12
#define QUEUE_HOSTS_INDEX            info->num_index+13
#define QUEUE_EXCLUSIVE_INDEX        info->num_index+14
#define QUEUE_DESCRIPTION_INDEX      info->num_index+15
#define QUEUE_MIG_INDEX              info->num_index+16
#define QUEUE_QJOBLIMIT_INDEX        info->num_index+17
#define QUEUE_DISPATCHWIN_INDEX      info->num_index+18
#define QUEUE_PROCLIMIT_INDEX        info->num_index+19
#define QUEUE_ADMINS_INDEX           info->num_index+20
#define QUEUE_PREEXEC_INDEX          info->num_index+21
#define QUEUE_POSTEXEC_INDEX         info->num_index+22
#define QUEUE_REQUEUE_EVALUES_INDEX  info->num_index+23
#define QUEUE_HJOBLIMIT_INDEX        info->num_index+24
#define QUEUE_RESREQ_INDEX           info->num_index+25
#define QUEUE_SLOTRESERVE_INDEX      info->num_index+26
#define QUEUE_RESUMECOND_INDEX       info->num_index+27
#define QUEUE_STOPCOND_INDEX         info->num_index+28
#define QUEUE_JOBSTARTER_INDEX       info->num_index+29
#define QUEUE_SWAPLIMIT_INDEX        info->num_index+30
#define QUEUE_PROCESSLIMIT_INDEX     info->num_index+31
#define QUEUE_JOBCONTROLS_INDEX      info->num_index+32
#define QUEUE_TERMINATEWHEN_INDEX    info->num_index+33
#define QUEUE_JOBSCHEDULEDELAY_INDEX info->num_index+34
#define QUEUE_INTERACTIVE_INDEX      info->num_index+35
#define QUEUE_JOBACCTINTERVAL_INDEX  info->num_index+36
#define QUEUE_BACKFILL_INDEX         info->num_index+37
#define QUEUE_IGNOREDEADLINE_INDEX   info->num_index+38
#define QUEUE_CHKPNT_INDEX           info->num_index+39
#define QUEUE_RERUNNABLE_INDEX       info->num_index+40
#define QUEUE_ROUNDROBIN_INDEX       info->num_index+41
#define QUEUE_PREPOSTUSER_INDEX      info->num_index+42
#define QUEUE_FAIRSHARE_INDEX        info->num_index+43
#define QUEUE_PREEMPTION_INDEX       info->num_index+44
#define QUEUE_OWNERSHIP_INDEX        info->num_index+45
#define QUEUE_MEMORYRESERVE_INDEX    info->num_index+46

    if (NULL == param[QUEUE_NAME_INDEX].string_value) {

        log_message(ERR, NOCLASS, "%s: In %s queue section, queue name must be specified.",
                    __func__, fname);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    if (0 == strcmp(param[QUEUE_NAME_INDEX].string_value, DEFAULT_STRING)) {

        log_message(ERR, NOCLASS, "%s: In %s queue section, queue name <%s> is a reserved keyword",
                    __func__, fname, param[QUEUE_NAME_INDEX].string_value);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    if (NULL != get_queueinfo(param[QUEUE_NAME_INDEX].string_value)) {

        log_message(ERR, NOCLASS, "%s: In %s queue section, queue name <%s> is multiply defined.",
                    __func__, fname, param[QUEUE_NAME_INDEX].string_value);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }

    return TRUE;
} // end function check_queue_definition

/**
 * @brief       Add queue to sptrQueueConf.
 *
 * @param[in]   param         #1: queue parameters
 * @param[in]   fname         #2: full path file name
 * @param[in]   info          #3: cluster resource table
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parameter has syntax error
 */
static bool_t
add_conf_queue(struct config_param *param, char *fname, struct quickpoolInfo *info)
{
    struct QuickPoolQueueInfo *queue=NULL;
    if (0 == sptrQueueConf->num_queues) {
        sptrQueueConf->queues = (struct QuickPoolQueueInfo *)calloc(1, sizeof(struct QuickPoolQueueInfo));
        if (NULL == sptrQueueConf->queues) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sizeof(struct QuickPoolQueueInfo));
            quickpool_errno = ERROR_MEMORY;

            return FALSE;
        }

        queue = &(sptrQueueConf->queues[0]);
    } else {
        sptrQueueConf->queues = (struct QuickPoolQueueInfo *)realloc(sptrQueueConf->queues, (sptrQueueConf->num_queues+1)*sizeof(struct QuickPoolQueueInfo));
        if (NULL == sptrQueueConf->queues) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "realloc", (sptrQueueConf->num_queues+1)*sizeof(struct QuickPoolQueueInfo));
            quickpool_errno = ERROR_MEMORY;

            return FALSE;
        }

        queue = &(sptrQueueConf->queues[sptrQueueConf->num_queues]);
    }

    sptrQueueConf->num_queues ++;

    initialize_queueinfo(queue);

    queue->queue = copy_string(param[QUEUE_NAME_INDEX].string_value);
    if (0 < param[QUEUE_PRIORITY_INDEX].index) {
        queue->priority = param[QUEUE_PRIORITY_INDEX].int_value;
    } else {
        queue->priority = 1;
    }

    if (param[QUEUE_NICE_INDEX].int_value < -INFINIT_SHORT
        || param[QUEUE_NICE_INDEX].int_value > INFINIT_SHORT) {

        log_message(ERR, NOCLASS, "%s: In %s queue section, nice value <%d> is not between [%d-%d].",
                    __func__, fname, param[QUEUE_NICE_INDEX].int_value,
                    -INFINIT_SHORT, INFINIT_SHORT);
        quickpool_errno = ERROR_CONF_WARNING;
    } else {
        queue->nice = param[QUEUE_NICE_INDEX].int_value;
    }

    if (0 < param[QUEUE_UJOBLIMIT_INDEX].index) {
        queue->user_slot_limit = param[QUEUE_UJOBLIMIT_INDEX].int_value;
    }

    if (0 < param[QUEUE_RUNWINDOW_INDEX].index && NULL != param[QUEUE_RUNWINDOW_INDEX].string_value) {

        queue->run_window = parse_time_window(param[QUEUE_RUNWINDOW_INDEX].string_value,
                                              NULL, "queue");
        if (NULL == queue->run_window) {
	    quickpool_errno = ERROR_CONF_WARNING;
        }
    }

    if (0 < param[QUEUE_CPULIMIT_INDEX].index) {
        parse_queue_limit(param[QUEUE_CPULIMIT_INDEX].param_name,
                          param[QUEUE_CPULIMIT_INDEX].string_value,
                          RESOURCE_LIMIT_CPU, queue, fname);
    }

    if (0 < param[QUEUE_FILELIMIT_INDEX].index) {
        parse_queue_limit(param[QUEUE_FILELIMIT_INDEX].param_name,
                          param[QUEUE_FILELIMIT_INDEX].string_value,
                          RESOURCE_LIMIT_FSIZE, queue, fname);
    }

    if (0 < param[QUEUE_DATALIMIT_INDEX].index) {
        parse_queue_limit(param[QUEUE_DATALIMIT_INDEX].param_name,
                          param[QUEUE_DATALIMIT_INDEX].string_value,
                          RESOURCE_LIMIT_DATA, queue, fname);
    }

    if (0 < param[QUEUE_STACKLIMIT_INDEX].index) {
        parse_queue_limit(param[QUEUE_STACKLIMIT_INDEX].param_name,
                          param[QUEUE_STACKLIMIT_INDEX].string_value,
                          RESOURCE_LIMIT_STACK, queue, fname);
    }

    if (0 < param[QUEUE_CORELIMIT_INDEX].index) {
        parse_queue_limit(param[QUEUE_CORELIMIT_INDEX].param_name,
                          param[QUEUE_CORELIMIT_INDEX].string_value,
                          RESOURCE_LIMIT_CORE, queue, fname);
    }

    if (0 < param[QUEUE_MEMLIMIT_INDEX].index) {
        parse_queue_limit(param[QUEUE_MEMLIMIT_INDEX].param_name,
                          param[QUEUE_MEMLIMIT_INDEX].string_value,
                          RESOURCE_LIMIT_RSS, queue, fname);
    }

    if (0 < param[QUEUE_RUNLIMIT_INDEX].index) {
        parse_queue_limit(param[QUEUE_RUNLIMIT_INDEX].param_name,
                          param[QUEUE_RUNLIMIT_INDEX].string_value,
                          RESOURCE_LIMIT_RUN, queue, fname);
    }

    if (0 < param[QUEUE_USERS_INDEX].index) {

        struct QuickPoolUserGroupInfo *queueUserGroup;
        queueUserGroup = (struct QuickPoolUserGroupInfo *)calloc(1, sizeof(struct QuickPoolUserGroupInfo));
        if (NULL == queueUserGroup) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sizeof(struct QuickPoolUserGroupInfo));
            quickpool_errno = ERROR_MEMORY;
            return FALSE;
        }

        queueUserGroup->group_name = copy_string(queue->queue);

        int ret;
        ret = parse_usergroup_member(param[QUEUE_USERS_INDEX].string_value, queueUserGroup,
                                     fname, " Queue section USERS");
        if (0 > ret && quickpool_errno == ERROR_MEMORY) {
            free_usergroup_info(queueUserGroup);
            return FALSE;
        }

        if (NULL == queueUserGroup->member_list
            || 0 == queueUserGroup->member_list->num_name) {

            log_message(ERR, NOCLASS, "%s: In %s queue section, no valid user or user group specified in USERS for <%s>, all users could submit jobs to this queue.",
                        __func__, fname, queue->queue);

            queue->users = copy_string(ALL_STRING);
        } else {
            unsigned int i, length=0;
            for (i=0; i<queueUserGroup->member_list->num_name; i++) {
                length += strlen(queueUserGroup->member_list->names[i]) + 1;
                length += 1;
            }

            if (NULL != queueUserGroup->exclude_members) {
                length += strlen(queueUserGroup->exclude_members) + 1;
            }
            length ++;

            queue->users = (char *)calloc(length, sizeof(char));
            if (NULL == queue->users) {
                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", length);
                quickpool_errno = ERROR_MEMORY;
                return FALSE;
            }

            for (i=0; i<queueUserGroup->member_list->num_name; i++) {
                if (0 == i) {
                    strcpy(queue->users, queueUserGroup->member_list->names[i]);
                } else {
                    strcat(queue->users, " ");
                    strcat(queue->users, queueUserGroup->member_list->names[i]);
                }
            }

            if (NULL != queueUserGroup->exclude_members) {
                strcat(queue->users, " ");
                strcat(queue->users, queueUserGroup->exclude_members);
            }
        }

        free_usergroup_info(queueUserGroup);
    }

    if (0 < param[QUEUE_HOSTS_INDEX].index) {
        char *sp = param[QUEUE_HOSTS_INDEX].string_value;
        unsigned int len = strlen(sp);
        char *host = (char *)calloc(len+1, sizeof(char));
        if (NULL == host) {

            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
            quickpool_errno = ERROR_MEMORY;

            return FALSE;
        }
        char *token;
        token = get_string_token(&sp, " ", host, len+1);
        while (NULL != token) {
            if (strcasecmp(token, "none") == 0) {
                log_message(ERR, NOCLASS, "%s: In %s queue section, \"none\" specified.",
	                    __func__, fname, queue->queue);
	        quickpool_errno = ERROR_CONF_WARNING;

                FREEUP(param[QUEUE_HOSTS_INDEX].string_value);
                break;
            }

            len = strlen(sp);
            token = get_string_token(&sp, " ", host, len+1);
        }
        FREEUP(host);

        struct QuickPoolHostGroupInfo *queueNodeGroup;
        queueNodeGroup = (struct QuickPoolHostGroupInfo *)calloc(1, sizeof(struct QuickPoolHostGroupInfo));
        if (NULL == queueNodeGroup) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sizeof(struct QuickPoolHostGroupInfo));
            quickpool_errno = ERROR_MEMORY;
            return FALSE;
        }

        queueNodeGroup->group_name = copy_string(queue->queue);

        int ret;
        ret = parse_nodegroup_member(param[QUEUE_HOSTS_INDEX].string_value, queueNodeGroup,
                                     fname, " Queue section HOSTS");
        if (0 > ret && quickpool_errno == ERROR_MEMORY) {
            free_nodegroup_info(queueNodeGroup);
            FREEUP(queueNodeGroup);
            return FALSE;
        }

        if (NULL == queueNodeGroup->member_list
            || 0 == queueNodeGroup->member_list->num_name) {

            log_message(ERR, NOCLASS, "%s: In %s queue section, no valid node or node group specified in HOSTS for <%s>, all nodes could accept jobs in this queue.",
                        __func__, fname, queue->queue);

            queue->hosts = copy_string(ALL_STRING);
        } else {
            unsigned int i, length=0;
            for (i=0; i<queueNodeGroup->member_list->num_name; i++) {
                length += strlen(queueNodeGroup->member_list->names[i]) + 1;
                length += 1;
            }

            if (NULL != queueNodeGroup->exclude_members) {
                length += strlen(queueNodeGroup->exclude_members) + 1;
            }
            length ++;

            queue->hosts = (char *)calloc(length, sizeof(char));
            if (NULL == queue->hosts) {
                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", length);
                quickpool_errno = ERROR_MEMORY;
                return FALSE;
            }

            for (i=0; i<queueNodeGroup->member_list->num_name; i++) {
                if (0 == i) {
                    strcpy(queue->hosts, queueNodeGroup->member_list->names[i]);
                } else {
                    strcat(queue->hosts, " ");
                    strcat(queue->hosts, queueNodeGroup->member_list->names[i]);
                }
            }

            if (NULL != queueNodeGroup->exclude_members) {
                strcat(queue->hosts, " ");
                strcat(queue->hosts, queueNodeGroup->exclude_members);
            }
        }

        free_nodegroup_info(queueNodeGroup);
        FREEUP(queueNodeGroup);
    }

    /* If ENABLE_EXCLUSIVE in qp.params is set to Y, set EXCLUSIVE for all queues by default.
     * Except the queues explicitly set to EXCLUSIVE=N.
     */
    if (param[QUEUE_EXCLUSIVE_INDEX].bool_value ||
        (gptrParamConf->param[PARAM_KEY_ENABLE_EXCLUSIVE].bool_value &&
        !(param[QUEUE_EXCLUSIVE_INDEX].index != -1 && param[QUEUE_EXCLUSIVE_INDEX].bool_value == FALSE))) {
        queue->schedule_policy |= QUEUE_POLICY_EXCLUSIVE;
    }

    if (0 < param[QUEUE_DESCRIPTION_INDEX].index) {
        queue->description = copy_string(param[QUEUE_DESCRIPTION_INDEX].string_value);
    }

    queue->mig = param[QUEUE_MIG_INDEX].int_value;

    if (0 < param[QUEUE_QJOBLIMIT_INDEX].index) {
        queue->max_num_slot = param[QUEUE_QJOBLIMIT_INDEX].int_value;
    }

    if (0 < param[QUEUE_DISPATCHWIN_INDEX].index
        && NULL != param[QUEUE_DISPATCHWIN_INDEX].string_value) {

        queue->dispatch_window = parse_time_window(param[QUEUE_DISPATCHWIN_INDEX].string_value,
                                                   NULL, "queue");
        if (NULL == queue->dispatch_window) {
	    quickpool_errno = ERROR_CONF_WARNING;
        }
    }

    if (0 < param[QUEUE_PROCLIMIT_INDEX].index) {
        if (!parse_job_slot_limit(param[QUEUE_PROCLIMIT_INDEX].string_value, queue, fname)) {
	    quickpool_errno = ERROR_CONF_WARNING;
        }
    }

    if (0 < param[QUEUE_ADMINS_INDEX].index) {
        queue->administrators = parse_queue_admins(param[QUEUE_ADMINS_INDEX].string_value,
                                                   fname, queue->queue);
        if (NULL == queue->administrators) {
	    quickpool_errno = ERROR_MEMORY;
            return FALSE;
        }

        strip_spaces(queue->administrators, FALSE);
        if (queue->administrators[0] == '\0') {
            log_message(ERR, NOCLASS, "%s: In %s queue section, no valid administrators <%s> specified for queue <%s>.",
	                __func__, fname, param[QUEUE_ADMINS_INDEX].string_value, queue->queue);

	    quickpool_errno = ERROR_CONF_WARNING;
            FREEUP(queue->administrators);
        }
    }

    if (0 < param[QUEUE_PREEXEC_INDEX].index) {
        queue->prejob_command = copy_string(param[QUEUE_PREEXEC_INDEX].string_value);
    }

    if (0 < param[QUEUE_POSTEXEC_INDEX].index) {
        queue->postjob_command = copy_string(param[QUEUE_POSTEXEC_INDEX].string_value);
    }

    if (0 < param[QUEUE_REQUEUE_EVALUES_INDEX].index) {

        queue->requeue_exitcode = parse_reque_evalues(param[QUEUE_REQUEUE_EVALUES_INDEX].string_value,
                                                      queue, fname);
    }

    if (0 < param[QUEUE_HJOBLIMIT_INDEX].index) {
        queue->host_slot_limit = param[QUEUE_HJOBLIMIT_INDEX].int_value;
    }

    if (0 < param[QUEUE_RESREQ_INDEX].index) {
        queue->resource_requirement = copy_string(param[QUEUE_RESREQ_INDEX].string_value);
    }

    if (0 < param[QUEUE_SLOTRESERVE_INDEX].index) {

        parse_queue_reservation(param[QUEUE_SLOTRESERVE_INDEX].string_value, queue, fname);
    }

    if (0 < param[QUEUE_RESUMECOND_INDEX].index) {
        queue->resume_condition = copy_string(param[QUEUE_RESUMECOND_INDEX].string_value);
    }

    if (0 < param[QUEUE_STOPCOND_INDEX].index) {
        queue->stop_condition = copy_string(param[QUEUE_STOPCOND_INDEX].string_value);
    }

    if (0 < param[QUEUE_JOBSTARTER_INDEX].index) {
        queue->job_starter = copy_string(param[QUEUE_JOBSTARTER_INDEX].string_value);
    }

    if (0 < param[QUEUE_SWAPLIMIT_INDEX].index) {
        parse_queue_limit(param[QUEUE_SWAPLIMIT_INDEX].param_name,
                          param[QUEUE_SWAPLIMIT_INDEX].string_value,
                          RESOURCE_LIMIT_SWAP, queue, fname);
    }

    if (0 < param[QUEUE_PROCESSLIMIT_INDEX].index) {
        parse_queue_limit(param[QUEUE_PROCESSLIMIT_INDEX].param_name,
                          param[QUEUE_PROCESSLIMIT_INDEX].string_value,
                          RESOURCE_LIMIT_PROCESS, queue, fname);
    }

    if (0 < param[QUEUE_JOBCONTROLS_INDEX].index) {
        parse_queue_actcmd(param[QUEUE_JOBCONTROLS_INDEX].string_value,
                           queue, fname, " queue section");
    }

    if (0 < param[QUEUE_TERMINATEWHEN_INDEX].index) {
        parse_queue_suspend(param[QUEUE_TERMINATEWHEN_INDEX].string_value,
                            queue, fname, " queue section");
    }

    if (0 < param[QUEUE_JOBSCHEDULEDELAY_INDEX].index) {
        queue->job_schedule_delay = param[QUEUE_JOBSCHEDULEDELAY_INDEX].int_value;
    }

    if (0 < param[QUEUE_JOBACCTINTERVAL_INDEX].index) {
        queue->job_dispatch_interval = param[QUEUE_JOBACCTINTERVAL_INDEX].int_value;
    }

    if (param[QUEUE_BACKFILL_INDEX].bool_value) {
        queue->schedule_policy |= QUEUE_POLICY_BACKFILL;
    }

    if (param[QUEUE_IGNOREDEADLINE_INDEX].bool_value) {
        queue->schedule_policy |= QUEUE_POLICY_IGNORE_DEADLINE;
    }

    if (0 < param[QUEUE_CHKPNT_INDEX].index) {

        int chkpntPrd=0;
        char dir[PATH_MAX];
        char prdstr[10];

        memset(prdstr, 0, 10);
        memset(dir, 0, sizeof(dir));

        sscanf(param[QUEUE_CHKPNT_INDEX].string_value, "%s %s", dir, prdstr);
        queue->chkpnt_dir = copy_string(dir);

        chkpntPrd = atoi(prdstr);
        if (chkpntPrd < 0) {

            log_message(ERR, NOCLASS ,"%s: In %s queue section, CHKPNT period of the queue <%s> is less than 0, set it to 0.", 
                        __func__, fname, queue->queue); 
            chkpntPrd = 0;
        }

        queue->schedule_policy |= QUEUE_POLICY_CHKPNT;
        queue->chkpnt_period = chkpntPrd * 60;
    }

    if (param[QUEUE_RERUNNABLE_INDEX].bool_value) {
        queue->schedule_policy |= QUEUE_POLICY_RERUNNABLE;
    }

    if (param[QUEUE_ROUNDROBIN_INDEX].bool_value) {
        queue->schedule_policy |= QUEUE_POLICY_ROUND_ROBIN;
    }

    if (0 < param[QUEUE_PREPOSTUSER_INDEX].index) {
        queue->prepost_user = copy_string(param[QUEUE_PREPOSTUSER_INDEX].string_value);
    }

    if (0 < param[QUEUE_FAIRSHARE_INDEX].index) {

        if (0 < param[QUEUE_OWNERSHIP_INDEX].index) {
            log_message(ERR, NOCLASS, "%s: In %s queue section, FAIRSHARE and OWNERSHIP cannot be specified together for queue %s.",
	                __func__, fname, queue->queue);
            FREEUP(queue->fairshare);
            FREEUP(queue->ownership);
            quickpool_errno = ERROR_CONF_WARNING;
        } else {
            if (strcasestr(param[QUEUE_FAIRSHARE_INDEX].string_value, "USER_SHARES")) {
                queue->fairshare = copy_string(param[QUEUE_FAIRSHARE_INDEX].string_value);
                queue->schedule_policy |= QUEUE_POLICY_FAIRSHARE;
            } else {
                log_message(ERR, NOCLASS, "%s: In %s queue section, USER_SHARES is not found in FAIRSHARE %s key for queue %s.",
	                    __func__, fname, param[QUEUE_FAIRSHARE_INDEX].string_value, queue->queue);
	        quickpool_errno = ERROR_CONF_WARNING;
            }
        }
    }

    if (0 < param[QUEUE_PREEMPTION_INDEX].index) {

        if (strcasestr(param[QUEUE_PREEMPTION_INDEX].string_value, "PREEMPTIVE")) {
            queue->preemption = copy_string(param[QUEUE_PREEMPTION_INDEX].string_value);
            queue->schedule_policy |= QUEUE_POLICY_PREEMPTIVE;
        } else {
            log_message(ERR, NOCLASS, "%s: In %s queue section, unsupported PREEMPTION %s key for queue %s.",
	                __func__, fname, param[QUEUE_PREEMPTION_INDEX].string_value, queue->queue);
	    quickpool_errno = ERROR_CONF_WARNING;
        }
    }

    if (0 < param[QUEUE_OWNERSHIP_INDEX].index) {

        if (strcasestr(param[QUEUE_OWNERSHIP_INDEX].string_value, "USERS")) {
            if (!parse_ownership(param[QUEUE_OWNERSHIP_INDEX].string_value, queue)) {
                log_message(ERR, NOCLASS, "%s: In %s queue section, missing '[' or ']' for OWNERSHIP %s of queue %s.",
                            __func__, fname, param[QUEUE_OWNERSHIP_INDEX].string_value, queue->queue);
                quickpool_errno = ERROR_CONF_WARNING;
                FREEUP(queue->ownership);
            } else {
                queue->schedule_policy |= QUEUE_POLICY_OWNERSHIP;
            }
        } else {
            log_message(ERR, NOCLASS, "%s: In %s queue section, unsupported OWNERSHIP %s key for queue %s.",
                        __func__, fname, param[QUEUE_OWNERSHIP_INDEX].string_value, queue->queue);
            quickpool_errno = ERROR_CONF_WARNING;
            FREEUP(queue->ownership);
        }
    }

    if (0 < param[QUEUE_MEMORYRESERVE_INDEX].index) {
        if (parse_queue_reservation(param[QUEUE_MEMORYRESERVE_INDEX].string_value, queue, fname)) {
            queue->schedule_policy |= QUEUE_POLICY_MEM_RESERVE;
        }
    }

    if (NULL == queue->load_schedule && info->num_index > 0) {
        queue->load_schedule = (float *)calloc(info->num_index, sizeof(float));
        if (NULL == queue->load_schedule) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", (info->num_index)*sizeof(float));
            quickpool_errno = ERROR_MEMORY;
            return FALSE;
        }
    }
    if (NULL == queue->load_stop && info->num_index > 0) {
        queue->load_stop = (float *)calloc(info->num_index, sizeof(float));
        if (NULL == queue->load_stop) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", (info->num_index)*sizeof(float));
            quickpool_errno = ERROR_MEMORY;
            return FALSE;
        }
    }

    read_threshold(info, param, queue->load_schedule, queue->load_stop,
                   fname, " queue section");

    return TRUE;
} // end function add_conf_queue

/**
 * @brief       Parse Queue section in qp.queues file.
 *
 * @param[in]   parser        #1: yaml parser hanlder
 * @param[in]   fname         #2: full path file name
 *
 * @retval      TRUE          #1: succeed
 * @retval      FALSE         #2: failed, parse error
 *
 * @note        parse, check and add
 */
static bool_t
parse_queue(yaml_parser_t *parser, char *fname, struct quickpoolInfo *info)
{
    yaml_token_t token;

    yaml_parser_scan(parser, &token);
    if (token.type != YAML_VALUE_TOKEN) {
        yaml_token_delete(&token);

        log_message(ERR, NOCLASS, "%s: there is format error in Queue section of file(%s) at line %d.",
                    __func__, fname, parser->mark.line);
        quickpool_errno = ERROR_CONF_WARNING;

        return FALSE;
    }
    yaml_token_delete(&token);

    struct config_param *queueParam;
    queueParam = (struct config_param *)calloc(info->num_index + 49, sizeof(struct config_param));
    if (NULL == queueParam) {
        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                    "calloc", (info->num_index+6)*sizeof(struct config_param));
        quickpool_errno = ERROR_MEMORY;

        return FALSE;
    }

    unsigned int i;
    for (i=0; i<info->num_index; i++) {

        queueParam[i].param_name = info->resource_table[i].name;
        queueParam[i].param_type = STRING_PARAM;
        queueParam[i].index = -1;
    }

    queueParam[QUEUE_NAME_INDEX].param_name = "QUEUE_NAME";
    queueParam[QUEUE_NAME_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_NAME_INDEX].index = -1;
    queueParam[QUEUE_PRIORITY_INDEX].param_name = "PRIORITY";
    queueParam[QUEUE_PRIORITY_INDEX].param_type = INT_PARAM;
    queueParam[QUEUE_PRIORITY_INDEX].index = -1;
    queueParam[QUEUE_NICE_INDEX].param_name = "NICE";
    queueParam[QUEUE_NICE_INDEX].param_type = INT_PARAM;
    queueParam[QUEUE_NICE_INDEX].index = -1;
    queueParam[QUEUE_UJOBLIMIT_INDEX].param_name = "UJOB_LIMIT";
    queueParam[QUEUE_UJOBLIMIT_INDEX].param_type = INT_PARAM;
    queueParam[QUEUE_UJOBLIMIT_INDEX].index = -1;
    queueParam[QUEUE_RUNWINDOW_INDEX].param_name = "RUN_WINDOW";
    queueParam[QUEUE_RUNWINDOW_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_RUNWINDOW_INDEX].index = -1;
    queueParam[QUEUE_CPULIMIT_INDEX].param_name = "CPULIMIT";
    queueParam[QUEUE_CPULIMIT_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_CPULIMIT_INDEX].index = -1;
    queueParam[QUEUE_FILELIMIT_INDEX].param_name = "FILELIMIT";
    queueParam[QUEUE_FILELIMIT_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_FILELIMIT_INDEX].index = -1;
    queueParam[QUEUE_DATALIMIT_INDEX].param_name = "DATALIMIT";
    queueParam[QUEUE_DATALIMIT_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_DATALIMIT_INDEX].index = -1;
    queueParam[QUEUE_STACKLIMIT_INDEX].param_name = "STACKLIMIT";
    queueParam[QUEUE_STACKLIMIT_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_STACKLIMIT_INDEX].index = -1;
    queueParam[QUEUE_CORELIMIT_INDEX].param_name = "CORELIMIT";
    queueParam[QUEUE_CORELIMIT_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_CORELIMIT_INDEX].index = -1;
    queueParam[QUEUE_MEMLIMIT_INDEX].param_name = "MEMLIMIT";
    queueParam[QUEUE_MEMLIMIT_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_MEMLIMIT_INDEX].index = -1;
    queueParam[QUEUE_RUNLIMIT_INDEX].param_name = "RUNLIMIT";
    queueParam[QUEUE_RUNLIMIT_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_RUNLIMIT_INDEX].index = -1;
    queueParam[QUEUE_USERS_INDEX].param_name = "USERS";
    queueParam[QUEUE_USERS_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_USERS_INDEX].index = -1;
    queueParam[QUEUE_HOSTS_INDEX].param_name = "HOSTS";
    queueParam[QUEUE_HOSTS_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_HOSTS_INDEX].index = -1;
    queueParam[QUEUE_EXCLUSIVE_INDEX].param_name = "EXCLUSIVE";
    queueParam[QUEUE_EXCLUSIVE_INDEX].param_type = BOOL_PARAM;
    queueParam[QUEUE_EXCLUSIVE_INDEX].index = -1;
    queueParam[QUEUE_DESCRIPTION_INDEX].param_name = "DESCRIPTION";
    queueParam[QUEUE_DESCRIPTION_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_DESCRIPTION_INDEX].index = -1;
    queueParam[QUEUE_MIG_INDEX].param_name = "MIG";
    queueParam[QUEUE_MIG_INDEX].param_type = INT_PARAM;
    queueParam[QUEUE_MIG_INDEX].index = -1;
    queueParam[QUEUE_QJOBLIMIT_INDEX].param_name = "QJOB_LIMIT";
    queueParam[QUEUE_QJOBLIMIT_INDEX].param_type = INT_PARAM;
    queueParam[QUEUE_QJOBLIMIT_INDEX].index = -1;
    queueParam[QUEUE_DISPATCHWIN_INDEX].param_name = "DISPATCH_WINDOW";
    queueParam[QUEUE_DISPATCHWIN_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_DISPATCHWIN_INDEX].index = -1;
    queueParam[QUEUE_PROCLIMIT_INDEX].param_name = "PROCLIMIT";
    queueParam[QUEUE_PROCLIMIT_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_PROCLIMIT_INDEX].index = -1;
    queueParam[QUEUE_ADMINS_INDEX].param_name = "ADMINISTRATORS";
    queueParam[QUEUE_ADMINS_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_ADMINS_INDEX].index = -1;
    queueParam[QUEUE_PREEXEC_INDEX].param_name = "PRE_EXEC";
    queueParam[QUEUE_PREEXEC_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_PREEXEC_INDEX].index = -1;
    queueParam[QUEUE_POSTEXEC_INDEX].param_name = "POST_EXEC";
    queueParam[QUEUE_POSTEXEC_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_POSTEXEC_INDEX].index = -1;
    queueParam[QUEUE_REQUEUE_EVALUES_INDEX].param_name = "REQUEUE_EXIT_VALUES";
    queueParam[QUEUE_REQUEUE_EVALUES_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_REQUEUE_EVALUES_INDEX].index = -1;
    queueParam[QUEUE_HJOBLIMIT_INDEX].param_name = "HJOB_LIMIT";
    queueParam[QUEUE_HJOBLIMIT_INDEX].param_type = INT_PARAM;
    queueParam[QUEUE_HJOBLIMIT_INDEX].index = -1;
    queueParam[QUEUE_RESREQ_INDEX].param_name = "RES_REQ";
    queueParam[QUEUE_RESREQ_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_RESREQ_INDEX].index = -1;
    queueParam[QUEUE_SLOTRESERVE_INDEX].param_name = "SLOT_RESERVE";
    queueParam[QUEUE_SLOTRESERVE_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_SLOTRESERVE_INDEX].index = -1;
    queueParam[QUEUE_RESUMECOND_INDEX].param_name = "RESUME_COND";
    queueParam[QUEUE_RESUMECOND_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_RESUMECOND_INDEX].index = -1;
    queueParam[QUEUE_STOPCOND_INDEX].param_name = "STOP_COND";
    queueParam[QUEUE_STOPCOND_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_STOPCOND_INDEX].index = -1;
    queueParam[QUEUE_JOBSTARTER_INDEX].param_name = "JOB_STARTER";
    queueParam[QUEUE_JOBSTARTER_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_JOBSTARTER_INDEX].index = -1;
    queueParam[QUEUE_SWAPLIMIT_INDEX].param_name = "SWAPLIMIT";
    queueParam[QUEUE_SWAPLIMIT_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_SWAPLIMIT_INDEX].index = -1;
    queueParam[QUEUE_PROCESSLIMIT_INDEX].param_name = "PROCESSLIMIT";
    queueParam[QUEUE_PROCESSLIMIT_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_PROCESSLIMIT_INDEX].index = -1;
    queueParam[QUEUE_JOBCONTROLS_INDEX].param_name = "JOB_CONTROLS";
    queueParam[QUEUE_JOBCONTROLS_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_JOBCONTROLS_INDEX].index = -1;
    queueParam[QUEUE_TERMINATEWHEN_INDEX].param_name = "TERMINATE_WHEN";
    queueParam[QUEUE_TERMINATEWHEN_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_TERMINATEWHEN_INDEX].index = -1;
    queueParam[QUEUE_JOBSCHEDULEDELAY_INDEX].param_name = "NEW_JOB_SCHED_DELAY";
    queueParam[QUEUE_JOBSCHEDULEDELAY_INDEX].param_type = INT_PARAM;
    queueParam[QUEUE_JOBSCHEDULEDELAY_INDEX].index = -1;
    queueParam[QUEUE_INTERACTIVE_INDEX].param_name = "INTERACTIVE";
    queueParam[QUEUE_INTERACTIVE_INDEX].param_type = BOOL_PARAM;
    queueParam[QUEUE_INTERACTIVE_INDEX].index = -1;
    queueParam[QUEUE_JOBACCTINTERVAL_INDEX].param_name = "JOB_ACCEPT_INTERVAL";
    queueParam[QUEUE_JOBACCTINTERVAL_INDEX].param_type = INT_PARAM;
    queueParam[QUEUE_JOBACCTINTERVAL_INDEX].index = -1;
    queueParam[QUEUE_BACKFILL_INDEX].param_name = "BACKFILL";
    queueParam[QUEUE_BACKFILL_INDEX].param_type = BOOL_PARAM;
    queueParam[QUEUE_BACKFILL_INDEX].index = -1;
    queueParam[QUEUE_IGNOREDEADLINE_INDEX].param_name = "IGNORE_DEADLINE";
    queueParam[QUEUE_IGNOREDEADLINE_INDEX].param_type = BOOL_PARAM;
    queueParam[QUEUE_IGNOREDEADLINE_INDEX].index = -1;
    queueParam[QUEUE_CHKPNT_INDEX].param_name = "CHKPNT";
    queueParam[QUEUE_CHKPNT_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_CHKPNT_INDEX].index = -1;
    queueParam[QUEUE_RERUNNABLE_INDEX].param_name = "RERUNNABLE";
    queueParam[QUEUE_RERUNNABLE_INDEX].param_type = BOOL_PARAM;
    queueParam[QUEUE_RERUNNABLE_INDEX].index = -1;
    queueParam[QUEUE_ROUNDROBIN_INDEX].param_name = "ROUND_ROBIN_POLICY";
    queueParam[QUEUE_ROUNDROBIN_INDEX].param_type = BOOL_PARAM;
    queueParam[QUEUE_ROUNDROBIN_INDEX].index = -1;
    queueParam[QUEUE_PREPOSTUSER_INDEX].param_name = "PRE_POST_EXEC_USER";
    queueParam[QUEUE_PREPOSTUSER_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_PREPOSTUSER_INDEX].index = -1;
    queueParam[QUEUE_FAIRSHARE_INDEX].param_name = "FAIRSHARE";
    queueParam[QUEUE_FAIRSHARE_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_FAIRSHARE_INDEX].index = -1;
    queueParam[QUEUE_PREEMPTION_INDEX].param_name = "PREEMPTION";
    queueParam[QUEUE_PREEMPTION_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_PREEMPTION_INDEX].index = -1;
    queueParam[QUEUE_OWNERSHIP_INDEX].param_name = "OWNERSHIP";
    queueParam[QUEUE_OWNERSHIP_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_OWNERSHIP_INDEX].index = -1;
    queueParam[QUEUE_MEMORYRESERVE_INDEX].param_name = "SLOT_MEMORY_RESERVE";
    queueParam[QUEUE_MEMORYRESERVE_INDEX].param_type = STRING_PARAM;
    queueParam[QUEUE_MEMORYRESERVE_INDEX].index = -1;

    char *strKey=NULL;
    unsigned int numMapping=0, numQueue=0;
    int index = -1;
    do {
        yaml_parser_scan(parser, &token);

        if (YAML_NO_ERROR != parser->error) {

            log_message(ERR, NOCLASS, "%s: error occurred when parsing queue section of file(%s) at line %d: %s",
                        __func__, fname, parser->mark.line, parser->problem);

            free_param_value(queueParam);
            FREEUP(queueParam);
            quickpool_errno = ERROR_CONF_WARNING;

            return FALSE;
        }

        switch(token.type) {
        case YAML_FLOW_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_MAPPING_END_TOKEN:
            numMapping --;
            break;
        case YAML_FLOW_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_FLOW_SEQUENCE_END_TOKEN:
            numMapping --;
            break;
        case YAML_BLOCK_SEQUENCE_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_MAPPING_START_TOKEN:
            numMapping ++;
            break;
        case YAML_BLOCK_END_TOKEN:
            numMapping --;
            break;
        case YAML_SCALAR_TOKEN:
            if (NULL == strKey) {
                index = match_param_key((char *)token.data.scalar.value, queueParam);
                if (index < 0) {
                    log_message(WARNING, NOCLASS, "%s: in file %s at line %d, unknown key(%s) is specified.",
                                __func__, fname, parser->mark.line, (char *)token.data.scalar.value);
                    quickpool_errno = ERROR_CONF_WARNING;
                    break;
                }

                strKey = copy_string((char *)token.data.scalar.value);
            } else {

                if (0 == strcasecmp(strKey, queueParam[QUEUE_NAME_INDEX].param_name)) {
                    numQueue ++;

                    if (numQueue > 1) {

                        if (check_queue_definition(queueParam, fname, info)) {
                            add_conf_queue(queueParam, fname, info);
                        }

                        reset_param_value(queueParam);
                    }
                }

                if (!set_param_value(index, (char *)token.data.scalar.value, queueParam)) {
                    log_message(ERR, NOCLASS, "%s: error occurred when parsing queue section of file(%s) at line %d: see above for reason",
                                __func__, fname, parser->mark.line);
                    quickpool_errno = ERROR_CONF_WARNING;
                }

                index = -1;
                FREEUP(strKey);
            }

            break;
        default:
            break;
        }

        if ((token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN) || numMapping > 0) {
            yaml_token_delete(&token);
        }
    } while (token.type != YAML_BLOCK_END_TOKEN && token.type != YAML_FLOW_MAPPING_END_TOKEN
             && token.type != YAML_FLOW_SEQUENCE_END_TOKEN);
    yaml_token_delete(&token);

    if (numQueue > 0 && check_queue_definition(queueParam, fname, info)) {
        add_conf_queue(queueParam, fname, info);
    }

    free_param_value(queueParam);
    FREEUP(queueParam);

    return TRUE;
} // end function parse_queue

static char **
expand_group(char *word, int *num)
{
    char **strMembers = NULL, **tempList;

    if (NULL == word || NULL == num) {
        return NULL;
    }

    struct QuickPoolUserGroupInfo *userGroup;
    userGroup = get_usergroup(word);
    if (NULL == userGroup) {

        strMembers = (char **)calloc(1, sizeof(char *));
        if (NULL == strMembers) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", sizeof(char *));
            quickpool_errno = ERROR_MEMORY;
            return NULL;
        }

        strMembers[0] = copy_string(word);
        *num = 1;
        return (strMembers);
    }

    struct nameList *memberList;
    memberList = userGroup->member_list;
    if (NULL == memberList || 0 == memberList->num_name) {

        strMembers = (char **)calloc(1, sizeof(char *));
        if (NULL == strMembers) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", sizeof(char *));
            quickpool_errno = ERROR_MEMORY;
            return NULL;
        }

        strMembers[0] = copy_string(ALL_STRING);
        *num = 1;
        return (strMembers);
    }

    unsigned int i, n=0;
    for (i=0; i<memberList->num_name; i++) {

        struct QuickPoolUserGroupInfo *subUserGroup;
        subUserGroup = get_usergroup(memberList->names[i]);
        if (NULL == subUserGroup) {

            if (NULL == strMembers) {
                tempList = (char **)calloc(n+1, sizeof(char *));
            } else {
                tempList = (char **)realloc(strMembers, (n+1)*sizeof(char *));
            }

            if (NULL == tempList) {
                log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                            "calloc", (n+1)*sizeof(char *));
	        quickpool_errno = ERROR_MEMORY;
                return NULL;
            }

            strMembers = tempList;

            strMembers[n] = copy_string(memberList->names[i]);
            n++;
        } else {
            int numSub=0;
            char **strSubMembers;
            strSubMembers = expand_group(memberList->names[i], &numSub);
            if (NULL == strSubMembers && quickpool_errno == ERROR_MEMORY) {
                return NULL;
            }

            if (0 < numSub) {

                if (!strcmp(strSubMembers[0], ALL_STRING)) {

                    if (NULL == strMembers) {
                        strMembers = (char **)calloc(1, sizeof(char *));
                        if (NULL == strMembers) {
                            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                        "calloc", sizeof(char *));
                            quickpool_errno = ERROR_MEMORY;
                            return NULL;
                        }
                    } else {
                        unsigned int k;
                        for (k=0; k<n; k++) {
                            FREEUP(strMembers[k]);
                        }
                    }

                    strMembers[0] = copy_string(ALL_STRING);
                    *num = 1;
                    return (strMembers);
                }

                if (NULL == strMembers) {
                    tempList = (char **)calloc(n+numSub, sizeof(char *));
                } else {
                    tempList = (char **)realloc(strMembers, (n+numSub)*sizeof(char *));
                }

                if (NULL == tempList) {
                    log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                                "calloc", (n+numSub)*sizeof(char *));
		    quickpool_errno = ERROR_MEMORY;
                    return NULL;
                }

                strMembers = tempList;

                int j;
                for (j=0; j<numSub; j++) {
                    strMembers[n+j] = strSubMembers[j];
                }
                n += numSub;
            }

            FREEUP(strSubMembers);
        }
    }

    *num = n;

    return (strMembers);
} // end function expand_group

static char *
parse_queue_admins(char *admins, char *fname, char *queue)
{
    if (NULL == admins) {
        log_message(ERR, NOCLASS, NULL_POINTER_STR, __func__,  "admins");
        return NULL;
    }

    char *expandAds;
    expandAds = (char *)calloc(PATH_MAX, sizeof(char));
    if (NULL == expandAds) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", PATH_MAX);
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }

    unsigned int len;
    len = PATH_MAX;

    char *sp;
    sp = admins;
    unsigned int length = strlen(sp);
    char *admin = (char *)calloc(length+1, sizeof(char));
    if (NULL == expandAds) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", length+1);
        quickpool_errno = ERROR_MEMORY;
        return NULL;
    }
    char *token;
    while ((token=get_string_token(&sp, " ", admin, length+1)) != NULL) {
        length = strlen(sp);

        if (0 == strcmp(token, ALL_STRING)) {

            if ((put_inlist(&expandAds, &len, ALL_STRING, IN_QUEUE_ADMIN_STR)) == NULL) {
                goto Error;
            }

            continue;
        }

        struct passwd *pw;
        pw = get_user_byname(token);
        if (NULL != pw) {

            if ((put_inlist(&expandAds, &len, pw->pw_name, IN_QUEUE_ADMIN_STR)) == NULL) {
                goto Error;
            }

            continue;
        }

        struct QuickPoolUserGroupInfo *uGroup;
        uGroup = get_usergroup(token);
        if (NULL != uGroup) {

            if (put_inlist(&expandAds, &len, uGroup->group_name, IN_QUEUE_ADMIN_STR) == NULL) {
                goto Error;
            }

            continue;
        }

        struct  group *osGroup;
        osGroup = get_group_byname(token);
        if (NULL != osGroup) {

            if (put_inlist(&expandAds, &len, token, IN_QUEUE_ADMIN_STR) == NULL) {
                goto Error;
            }

            continue;
        }

        log_message(WARNING, NOCLASS, "%s: In %s queue section, unknown user or user group name <%s> for queue %s; Maybe a windows user or of another domain.",
                    __func__, fname, token, queue);

        if ((put_inlist(&expandAds, &len, token, IN_QUEUE_ADMIN_STR)) == NULL) {
            goto Error;
        }
    }
    FREEUP(admin);

    return (expandAds);

Error:
    log_message(ERR, NOCLASS, STR_FUNC_FAIL, __func__, "put_inlist");
    FREEUP(expandAds);
    FREEUP(admin);
    quickpool_errno = ERROR_MEMORY;

    return NULL;
}

static int
isin_list(char *list, const char *string)
{
    if (list == NULL || string == NULL || list[0] == '\0' || string[0] == '\0')
        return FALSE;

    char *sp;
    sp = list;

    unsigned int len = strlen(sp);
    char *name = (char *)calloc(len+1, sizeof(char));
    if (NULL == name) {

        log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", len+1);
        quickpool_errno = ERROR_MEMORY;
        return FALSE;
    }
    char *token;
    while ((token=get_string_token(&sp, " ", name, len+1)) != NULL) {
        len = strlen(sp);

        if (strcmp(string, token) == 0)
            return TRUE;
    }
    FREEUP(name);

    return FALSE;
}

static char *
put_inlist(char **list, unsigned int *len, const char *string, const char *listName)
{
    char *sp;
    unsigned int length = *len;

    if (string == NULL)
        return (*list);

    if (list == (char **) NULL)
        return NULL;

    sp = copy_string(listName);
    if (isin_list(*list, string)) {
        log_message(ERR, NOCLASS, "%s: %s is repeatedly specified %s.", __func__,  string, sp);
        FREEUP(sp);
        return (*list);
    }

    if (length <= strlen(*list) + strlen(string) + 2) {
        char *temp;

        if (NULL == *list) {
            temp = (char *)calloc(2*length, sizeof(char));
        } else {
            temp = (char *)realloc(*list, 2*length);
        }

        if (NULL == temp) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__, "calloc", 2*length);
            FREEUP(*list);
            quickpool_errno = ERROR_MEMORY;
            return NULL;
        }

        *len *=2;
        length *= 2;
        *list = temp;
    }
    FREEUP(sp);
    strcat(*list, string);
    strcat(*list, " ");

    return (*list);
}

static int
create_userconf(void)
{
    if (NULL == sptrUserConf) {

        sptrUserConf = (struct userConf *)calloc(1, sizeof(struct userConf));
        if (NULL == sptrUserConf) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sizeof(struct userConf));
            quickpool_errno = ERROR_CONF_FATAL;
            return -1;
        }
    }

    return 0;
}

static int
create_hostconf(void)
{
    if (NULL == sptrHostConf) {
        sptrHostConf = (struct hostConf *)calloc(1, sizeof(struct hostConf));
        if (NULL == sptrHostConf) {
            log_message(ERR, NOCLASS, STR_FUNC_D_FAIL_M, __func__,
                        "calloc", sizeof(struct hostConf));
            quickpool_errno = ERROR_CONF_FATAL;
            return -1;
        }
    }

    return 0;
}

/**
 * @brief       Intialize the resource list. 
 *
 * @param       none
 *
 * @retval      -1            #1: calloc fail.
 * @retval       0            #2: succeed.
 *
 * @note        Put all built-in resources in the list after initialization.
 */
static int
initialize_resource_table(void)
{
    sintMaxResource = 1000;

    struct resource_definition *resTable;
    resTable = (struct resource_definition *)calloc(sintMaxResource,
                                                    sizeof(struct resource_definition));
    if (NULL == resTable) {
        log_message(ERR, NOCLASS, STR_FUNC_FAIL_M, __func__, "calloc" );
        return -1;
    }

    int i=0;
    qpinfo.num_index = 0;
    qpinfo.num_user_index = 0;
    while (NULL != builtInIndices[i].name) {

        resTable[i].name = copy_string(builtInIndices[i].name);
        resTable[i].des = copy_string(builtInIndices[i].des);
        resTable[i].value_type = builtInIndices[i].value_type;
        resTable[i].order_type = builtInIndices[i].order_type;
        resTable[i].interval = builtInIndices[i].interval;
        resTable[i].flags = builtInIndices[i].flags;

        if (resTable[i].flags &  RESOURCE_FLAG_DYNAMIC) {
            qpinfo.num_index++;
        }

        i++;
    }

    qpinfo.num_resources = i;
    qpinfo.resource_table = resTable;

    return 0;
} // end function initialize_resource_table
